gloomberb 0.19.0 → 0.19.2
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +2 -1
- package/patches/electrobun@1.18.1.patch +23 -1
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/data.ts +2 -1
- package/src/api-client/errors.ts +38 -0
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +4 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +12 -0
- package/src/app/runtime/startup.ts +3 -1
- package/src/architecture/core-builtin-allowlist.json +2 -2
- package/src/capabilities/chart-series.ts +2 -1
- package/src/cli/commands/market.ts +44 -16
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/overview.ts +84 -19
- package/src/cli/commands/search.ts +13 -13
- package/src/cli/commands/ticker.ts +32 -2
- package/src/cli/context.ts +1 -0
- package/src/cli/errors.ts +17 -2
- package/src/cli/glossary.ts +8 -1
- package/src/cli/history-rows.ts +35 -7
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/known-gaps.ts +11 -0
- package/src/cli/listing-arg.ts +74 -11
- package/src/cli/pane-functions/capabilities.ts +1 -0
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/freshness.ts +7 -1
- package/src/cli/pane-functions/function-help.ts +7 -0
- package/src/cli/pane-functions/index.ts +6 -3
- package/src/cli/quote-answer.ts +30 -7
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/components/command-bar/list/model.ts +5 -0
- package/src/components/command-bar/routes/root/runtime.ts +29 -8
- package/src/components/command-bar/selection-runtime.ts +64 -32
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/layout/detached-pane-shell.tsx +2 -2
- package/src/components/layout/header.tsx +2 -2
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +23 -6
- package/src/components/layout/pane/footer/model.ts +116 -16
- package/src/components/layout/pane/header.tsx +2 -2
- package/src/components/layout/pane/status-footer.ts +37 -3
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-frame.tsx +2 -1
- package/src/components/onboarding/onboarding-wizard.tsx +6 -3
- package/src/components/ticker/list-table-view.tsx +4 -20
- package/src/components/ui/display.tsx +29 -3
- package/src/components/ui/icon.tsx +12 -3
- package/src/core/app-services.ts +11 -2
- package/src/data/app-persistence.ts +9 -7
- package/src/data/plugin-cache.ts +20 -3
- package/src/data/plugin-state-store.ts +81 -24
- package/src/data/resource-store.ts +130 -58
- package/src/data/saved-copies.ts +8 -0
- package/src/data/session-store.ts +31 -8
- package/src/data/sqlite/access.ts +313 -0
- package/src/data/sqlite/checkpointer.ts +198 -0
- package/src/data/sqlite/database.ts +47 -4
- package/src/data/sqlite/retry.ts +50 -9
- package/src/data/ticker-store.ts +57 -23
- package/src/docs/snapshot.json +32 -5
- package/src/i18n/es.ts +2 -4
- package/src/i18n/ja.ts +2 -4
- package/src/i18n/ko.ts +2 -4
- package/src/i18n/zh-cn.ts +2 -4
- package/src/i18n/zh-tw.ts +2 -4
- package/src/market-data/depositary-pair.ts +25 -1
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/market-data/realized-volatility.ts +4 -2
- package/src/news/aggregator.ts +7 -1
- package/src/news/types.ts +6 -0
- package/src/plugins/auto-update-node.ts +10 -6
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli.ts +34 -11
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +15 -6
- package/src/plugins/builtin/alerts/headless.ts +10 -6
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
- package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +5 -0
- package/src/plugins/builtin/chart-composer/index.tsx +4 -5
- package/src/plugins/builtin/chart-composer/pane.tsx +5 -1
- package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -22
- package/src/plugins/builtin/clinical-trials/pane.tsx +46 -27
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +87 -9
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/commodity-balances/client.ts +4 -1
- package/src/plugins/builtin/company-attention/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/pane.tsx +3 -0
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -1
- package/src/plugins/builtin/cot/client.ts +28 -2
- package/src/plugins/builtin/cot/headless.ts +4 -1
- package/src/plugins/builtin/cot/index.tsx +4 -2
- package/src/plugins/builtin/cot/model.ts +58 -7
- package/src/plugins/builtin/cot/pane.tsx +6 -2
- package/src/plugins/builtin/cpi/client.ts +6 -5
- package/src/plugins/builtin/credit-boards/series.ts +3 -4
- package/src/plugins/builtin/credit-conditions/client.ts +22 -14
- package/src/plugins/builtin/credit-documents/headless.ts +3 -3
- package/src/plugins/builtin/crypto-board/client.ts +5 -5
- package/src/plugins/builtin/crypto-board/series.ts +4 -4
- package/src/plugins/builtin/debt-maturities/series.ts +3 -3
- package/src/plugins/builtin/doe/client.ts +6 -5
- package/src/plugins/builtin/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- package/src/plugins/builtin/doe/prices.ts +2 -3
- package/src/plugins/builtin/doe/series.ts +4 -4
- package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
- package/src/plugins/builtin/earnings-calls/data.ts +37 -17
- package/src/plugins/builtin/earnings-calls/headless.ts +13 -3
- package/src/plugins/builtin/earnings-calls/pane.tsx +34 -14
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +18 -4
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +10 -2
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +3 -0
- package/src/plugins/builtin/executives/data.ts +6 -1
- package/src/plugins/builtin/executives/pane.tsx +9 -21
- package/src/plugins/builtin/fear-greed/cache.ts +4 -0
- package/src/plugins/builtin/fear-greed/series.ts +6 -4
- package/src/plugins/builtin/gpu/client.ts +4 -1
- package/src/plugins/builtin/gpu/series.ts +2 -3
- package/src/plugins/builtin/help/index.tsx +61 -59
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +4 -2
- package/src/plugins/builtin/holders/headless.ts +4 -2
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- package/src/plugins/builtin/jodi/client.ts +4 -1
- package/src/plugins/builtin/macro-day/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- package/src/plugins/builtin/market-movers/client.ts +3 -0
- package/src/plugins/builtin/market-movers/headless.ts +3 -1
- package/src/plugins/builtin/market-movers/index.tsx +10 -5
- package/src/plugins/builtin/market-movers/screener.ts +27 -6
- package/src/plugins/builtin/market-movers/session-body.tsx +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/money-markets/series.ts +3 -5
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +3 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +2 -6
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +2 -1
- package/src/plugins/builtin/portfolio-list/table.tsx +4 -6
- package/src/plugins/builtin/power/gas-client.ts +2 -1
- package/src/plugins/builtin/power/prices-client.ts +3 -2
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +9 -7
- package/src/plugins/builtin/research/event-model.ts +30 -4
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +8 -25
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +52 -15
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- package/src/plugins/builtin/risk-factors/data.ts +12 -6
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/risk-factors/index.tsx +3 -1
- package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- package/src/plugins/builtin/scanner/live-headless.ts +21 -2
- package/src/plugins/builtin/scanner/lock-note.ts +26 -3
- package/src/plugins/builtin/sec/headless.ts +2 -1
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +20 -7
- package/src/plugins/builtin/shared/fred-points.ts +6 -1
- package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
- package/src/plugins/builtin/shared/report-freshness.ts +8 -0
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- package/src/plugins/builtin/shared/series-warning.ts +13 -4
- package/src/plugins/builtin/short-volume/series.ts +2 -3
- package/src/plugins/builtin/social-mentions/client.ts +15 -10
- package/src/plugins/builtin/social-mentions/series.ts +2 -3
- package/src/plugins/builtin/thirteenf/api.ts +15 -9
- package/src/plugins/builtin/thirteenf/data.ts +39 -11
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
- package/src/plugins/builtin/thirteenf/pane.tsx +21 -5
- package/src/plugins/builtin/thirteenf/types.ts +2 -0
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +5 -2
- package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
- package/src/plugins/builtin/ticker-detail/headless.ts +59 -8
- package/src/plugins/builtin/ticker-detail/overview/model.ts +10 -2
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +11 -6
- package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
- package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
- package/src/plugins/builtin/treasury-auctions/headless.ts +24 -13
- package/src/plugins/builtin/treasury-auctions/pane.tsx +18 -6
- package/src/plugins/builtin/volatility/client.ts +43 -16
- package/src/plugins/builtin/volatility/headless.ts +5 -2
- package/src/plugins/builtin/volatility/model.ts +17 -6
- package/src/plugins/builtin/volatility/pane.tsx +11 -4
- package/src/plugins/builtin/volatility/series.ts +2 -1
- package/src/plugins/builtin/yield-curve/client.ts +59 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/installer.ts +50 -39
- package/src/portfolio/column-values.ts +11 -7
- package/src/renderers/dom/desktop/icons.tsx +10 -2
- package/src/renderers/electrobun/bun/desktop/backend.ts +9 -10
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +8 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +9 -1
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/attention.ts +54 -0
- package/src/renderers/electrobun/bun/window/create.ts +45 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/launcher/preload.ts +13 -0
- package/src/renderers/electrobun/launcher/windows-emulation.ts +50 -0
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/news-source.ts +2 -0
- package/src/renderers/electrobun/view/ui-host.tsx +16 -1
- package/src/series/resolve.ts +2 -1
- package/src/series/types.ts +5 -0
- package/src/sources/gloomberb-cloud/fred-series.ts +8 -4
- package/src/sources/gloomberb-cloud/index.ts +42 -8
- package/src/telemetry/stall-reports-node.ts +13 -2
- package/src/telemetry/stall-reports.ts +189 -13
- package/src/test-support/outage.ts +8 -0
- package/src/tickers/company-name-listing.ts +110 -40
- package/src/tickers/company-name.ts +71 -2
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/search/home-listing.ts +93 -8
- package/src/tickers/search/index.ts +7 -3
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/market.ts +5 -3
- package/src/time-series/price-comparison.ts +20 -2
- package/src/time-series/resolve.ts +20 -1
- package/src/time-series/types.ts +7 -0
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +45 -3
- package/src/types/headless.ts +6 -0
- package/src/ui/host.tsx +6 -1
- package/src/utils/crypto-pair.ts +121 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/financial-statements.ts +73 -16
- package/src/utils/fundamentals.ts +53 -10
- package/src/utils/lease-liabilities.ts +54 -0
- package/src/utils/main-thread-stall.ts +47 -2
- package/src/utils/perf-marks.ts +23 -11
- package/src/utils/price-history-integrity.ts +47 -1
- package/src/version.ts +2 -2
|
@@ -45,6 +45,7 @@ import { commentLettersPlugin } from "./builtin/comment-letters";
|
|
|
45
45
|
import { openFdaPlugin } from "./builtin/openfda";
|
|
46
46
|
import { browserGloomberbCloudPlugin } from "./builtin/cloud/browser";
|
|
47
47
|
import { changelogModule } from "./builtin/changelog";
|
|
48
|
+
import { roadmapModule } from "./builtin/roadmap";
|
|
48
49
|
import { chartComposerModule } from "./builtin/chart-composer";
|
|
49
50
|
import { connectionsModule } from "./builtin/connections";
|
|
50
51
|
import { relativeRotationModule } from "./builtin/relative-rotation";
|
|
@@ -126,7 +127,7 @@ import { FIXED_INCOME_HOSTS } from "./builtin/fixed-income/model";
|
|
|
126
127
|
|
|
127
128
|
const browserApplicationPlugin = composeBuiltinPlugin({
|
|
128
129
|
...applicationPluginMeta,
|
|
129
|
-
modules: [layoutManagerModule, helpModule, changelogModule, connectionsModule, docsModule],
|
|
130
|
+
modules: [layoutManagerModule, helpModule, changelogModule, connectionsModule, docsModule, roadmapModule],
|
|
130
131
|
});
|
|
131
132
|
|
|
132
133
|
const runtimeOnly = (module: PluginModule): PluginModule => {
|
package/src/plugins/host-link.ts
CHANGED
|
@@ -32,7 +32,7 @@ const LINKED_PACKAGES = ["gloomberb", "react"] as const;
|
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
34
|
* The names the host is published under: `gloomberb`, which plugins import
|
|
35
|
-
* as, and `@gloom-sh/terminal
|
|
35
|
+
* as, and `@gloom-sh/terminal`.
|
|
36
36
|
*/
|
|
37
37
|
const HOST_PACKAGE_NAMES: ReadonlySet<string> = new Set(["gloomberb", "@gloom-sh/terminal"]);
|
|
38
38
|
|
package/src/plugins/installer.ts
CHANGED
|
@@ -141,7 +141,13 @@ function run(
|
|
|
141
141
|
/** Whether a `git` command is on the PATH; plugins from GitHub need one to install or update. */
|
|
142
142
|
export function gitInstalled(): boolean {
|
|
143
143
|
if (typeof Bun !== "undefined") return Bun.which("git") !== null;
|
|
144
|
-
|
|
144
|
+
try {
|
|
145
|
+
execFileSync("git", ["--version"], { stdio: "ignore" });
|
|
146
|
+
return true;
|
|
147
|
+
} catch {
|
|
148
|
+
// Intentional ignore: a git that does not start is the answer this asks for.
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
145
151
|
}
|
|
146
152
|
|
|
147
153
|
/** What a plugin install says when there is no git to run: what to install, then to try again. */
|
|
@@ -171,14 +177,9 @@ async function git(args: string[], cwd: string, quiet: boolean, failure: string)
|
|
|
171
177
|
return result.stdout.trim();
|
|
172
178
|
}
|
|
173
179
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
return true;
|
|
178
|
-
} catch {
|
|
179
|
-
// Intentional ignore: Detached HEAD is expected after a pinned install.
|
|
180
|
-
return false;
|
|
181
|
-
}
|
|
180
|
+
/** False on a detached HEAD, which a pinned install leaves. */
|
|
181
|
+
function isOnBranch(dir: string): Promise<boolean> {
|
|
182
|
+
return gitSucceedsAsync(["symbolic-ref", "-q", "HEAD"], dir);
|
|
182
183
|
}
|
|
183
184
|
|
|
184
185
|
function pinMatches(commit: string | null, expected: string | undefined): boolean {
|
|
@@ -208,27 +209,27 @@ async function checkoutCommit(targetDir: string, commit: string, quiet: boolean)
|
|
|
208
209
|
await git(["checkout", "--detach", "--force", commit], targetDir, quiet, `Could not check out ${commit.slice(0, 7)}`);
|
|
209
210
|
}
|
|
210
211
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
}
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* The update path runs in the app (the terminal, the desktop's Bun process),
|
|
215
|
+
* where a sync git call holds the thread for the whole process start: tens of
|
|
216
|
+
* milliseconds each on Windows, several per plugin. These never block it.
|
|
217
|
+
*/
|
|
218
|
+
async function gitSucceedsAsync(args: string[], cwd: string): Promise<boolean> {
|
|
219
|
+
const result = await run("git", args, cwd, { stdout: "pipe", stderr: "pipe" })
|
|
220
|
+
.catch(logFailure("plugin-installer", "Could not run git", null));
|
|
221
|
+
return result?.code === 0;
|
|
219
222
|
}
|
|
220
223
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
return null;
|
|
227
|
-
}
|
|
224
|
+
/** Null when git fails: shallow or manifest-free checkouts may lack historical metadata. */
|
|
225
|
+
async function gitOutputAsync(args: string[], cwd: string): Promise<string | null> {
|
|
226
|
+
const result = await run("git", args, cwd, { stdout: "pipe", stderr: "pipe" })
|
|
227
|
+
.catch(logFailure("plugin-installer", "Could not run git", null));
|
|
228
|
+
return result?.code === 0 ? result.stdout.trim() : null;
|
|
228
229
|
}
|
|
229
230
|
|
|
230
|
-
function versionAt(dir: string, commit: string): string | null {
|
|
231
|
-
const text =
|
|
231
|
+
async function versionAt(dir: string, commit: string): Promise<string | null> {
|
|
232
|
+
const text = await gitOutputAsync(["show", `${commit}:package.json`], dir);
|
|
232
233
|
if (!text) return null;
|
|
233
234
|
try {
|
|
234
235
|
const version = JSON.parse(text).version;
|
|
@@ -247,15 +248,15 @@ function versionAt(dir: string, commit: string): string | null {
|
|
|
247
248
|
* it; otherwise the versions the two commits declare decide, then, when
|
|
248
249
|
* those match, their commit dates.
|
|
249
250
|
*/
|
|
250
|
-
export function describeOlderCommit(dir: string, installed: string, candidate: string, label = candidate.slice(0, 7)): string | null {
|
|
251
|
-
if (installed === candidate ||
|
|
252
|
-
const installedVersion = versionAt(dir, installed);
|
|
253
|
-
const byVersion = compareSemver(versionAt(dir, candidate), installedVersion);
|
|
251
|
+
export async function describeOlderCommit(dir: string, installed: string, candidate: string, label = candidate.slice(0, 7)): Promise<string | null> {
|
|
252
|
+
if (installed === candidate || await gitSucceedsAsync(["merge-base", "--is-ancestor", installed, candidate], dir)) return null;
|
|
253
|
+
const installedVersion = await versionAt(dir, installed);
|
|
254
|
+
const byVersion = compareSemver(await versionAt(dir, candidate), installedVersion);
|
|
254
255
|
const older = () => `the registry's ${label} is older than the installed ${byVersion ? installedVersion : installed.slice(0, 7)}`;
|
|
255
|
-
if (
|
|
256
|
+
if (await gitSucceedsAsync(["merge-base", "--is-ancestor", candidate, installed], dir)) return older();
|
|
256
257
|
if (byVersion !== null && byVersion !== 0) return byVersion < 0 ? older() : null;
|
|
257
|
-
const candidateTime = Number(
|
|
258
|
-
const installedTime = Number(
|
|
258
|
+
const candidateTime = Number(await gitOutputAsync(["show", "-s", "--format=%ct", candidate], dir));
|
|
259
|
+
const installedTime = Number(await gitOutputAsync(["show", "-s", "--format=%ct", installed], dir));
|
|
259
260
|
return candidateTime > 0 && installedTime > 0 && candidateTime < installedTime ? older() : null;
|
|
260
261
|
}
|
|
261
262
|
|
|
@@ -403,19 +404,29 @@ export interface PluginUpdateResult extends PluginDirectoryInfo {
|
|
|
403
404
|
kept?: string;
|
|
404
405
|
}
|
|
405
406
|
|
|
407
|
+
function githubRepoFromUrl(url: string): string | null {
|
|
408
|
+
const match = /github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?$/i.exec(url.trim());
|
|
409
|
+
return match ? `${match[1]}/${match[2]}` : null;
|
|
410
|
+
}
|
|
411
|
+
|
|
406
412
|
/** `owner/repo` from the checkout's origin remote, or null for anything that is not a GitHub clone. */
|
|
407
413
|
export function readPluginRemote(pluginDir: string): string | null {
|
|
408
414
|
try {
|
|
409
|
-
|
|
410
|
-
.toString("utf-8")
|
|
411
|
-
const match = /github\.com[:/]([^/]+)\/([^/]+?)(?:\.git)?$/i.exec(url);
|
|
412
|
-
return match ? `${match[1]}/${match[2]}` : null;
|
|
415
|
+
return githubRepoFromUrl(execFileSync("git", ["config", "--get", "remote.origin.url"], { cwd: pluginDir, stdio: ["ignore", "pipe", "ignore"] })
|
|
416
|
+
.toString("utf-8"));
|
|
413
417
|
} catch {
|
|
414
418
|
// Intentional ignore: Hand-copied plugins and local repositories may have no origin remote.
|
|
415
419
|
return null;
|
|
416
420
|
}
|
|
417
421
|
}
|
|
418
422
|
|
|
423
|
+
/** `readPluginRemote` without holding the thread, for the app's own update check. */
|
|
424
|
+
export async function readPluginRemoteAsync(pluginDir: string): Promise<string | null> {
|
|
425
|
+
// No origin remote (a hand-copied plugin, a local repository) is a failed git, and no repo.
|
|
426
|
+
const url = await gitOutputAsync(["config", "--get", "remote.origin.url"], pluginDir);
|
|
427
|
+
return url ? githubRepoFromUrl(url) : null;
|
|
428
|
+
}
|
|
429
|
+
|
|
419
430
|
/** The sha from `git ls-remote origin HEAD`; null when the answer is not one. */
|
|
420
431
|
export function parseRemoteHead(output: string): string | null {
|
|
421
432
|
const sha = output.trim().split(/\s+/)[0] ?? "";
|
|
@@ -500,7 +511,7 @@ export async function updatePlugin(name: string, options: PluginInstallOptions =
|
|
|
500
511
|
const commit = await fetchPin(targetDir, pin, quiet);
|
|
501
512
|
// Updates only move forward: a checkout installed from the default
|
|
502
513
|
// branch can be ahead of the tag the registry reviewed.
|
|
503
|
-
const kept = before ? describeOlderCommit(targetDir, before, commit, pin.ref ?? commit.slice(0, 7)) : null;
|
|
514
|
+
const kept = before ? await describeOlderCommit(targetDir, before, commit, pin.ref ?? commit.slice(0, 7)) : null;
|
|
504
515
|
if (kept) {
|
|
505
516
|
if (!quiet) console.log(cliStyles.muted(`Kept ${name}: ${kept}.`));
|
|
506
517
|
return { ...describe(targetDir, name, null), before, changed: false, kept };
|
|
@@ -510,7 +521,7 @@ export async function updatePlugin(name: string, options: PluginInstallOptions =
|
|
|
510
521
|
// Not in the registry, so there is nothing reviewed to land on: follow the
|
|
511
522
|
// remote's default branch, whether the checkout is on a branch or detached
|
|
512
523
|
// from an earlier pin.
|
|
513
|
-
if (isOnBranch(targetDir)) {
|
|
524
|
+
if (await isOnBranch(targetDir)) {
|
|
514
525
|
await git(["pull", "--ff-only"], targetDir, quiet, `Failed to update ${name}`);
|
|
515
526
|
} else {
|
|
516
527
|
await git(["fetch", "--depth", "1", "origin", "HEAD"], targetDir, quiet, `Failed to fetch ${name}`);
|
|
@@ -185,15 +185,19 @@ function positionSideLabel(ticker: TickerRecord, activeTab: string | undefined):
|
|
|
185
185
|
return "MIX";
|
|
186
186
|
}
|
|
187
187
|
|
|
188
|
-
/**
|
|
188
|
+
/**
|
|
189
|
+
* The currency a row is listed in: its position's, else its quote's or the one saved with it. An index's level is in
|
|
190
|
+
* points and has none, and a row whose currency no source gave (a coin pair before its quote) shows none, never the
|
|
191
|
+
* dollars it is valued in meanwhile: a row not held has no position currency, only that fallback.
|
|
192
|
+
*/
|
|
189
193
|
function listedCurrency(
|
|
190
|
-
|
|
191
|
-
quoteCurrency: string,
|
|
194
|
+
metrics: Pick<PortfolioPositionMetrics, "positionCount" | "positionCurrency">,
|
|
192
195
|
ticker: TickerRecord,
|
|
193
196
|
quote: TickerFinancials["quote"],
|
|
194
197
|
): string {
|
|
195
|
-
|
|
196
|
-
|
|
198
|
+
const held = metrics.positionCount > 0 ? metrics.positionCurrency : "";
|
|
199
|
+
if (!held && isIndexLevel(quote?.instrumentType || ticker.metadata.assetCategory)) return "";
|
|
200
|
+
return (held || quote?.currency || ticker.metadata.currency || "").toUpperCase();
|
|
197
201
|
}
|
|
198
202
|
|
|
199
203
|
function compactText(value: string | null | undefined): string {
|
|
@@ -458,7 +462,7 @@ export function getColumnValue(
|
|
|
458
462
|
),
|
|
459
463
|
};
|
|
460
464
|
case "currency":
|
|
461
|
-
return { text: compactText(currencyCodeLabel(listedCurrency(
|
|
465
|
+
return { text: compactText(currencyCodeLabel(listedCurrency(positionMetrics, ticker, quote))) };
|
|
462
466
|
case "sector":
|
|
463
467
|
return { text: compactText(ticker.metadata.sector || financials?.profile?.sector) };
|
|
464
468
|
case "industry":
|
|
@@ -713,7 +717,7 @@ export function getSortValue(
|
|
|
713
717
|
?? ticker.metadata.exchange
|
|
714
718
|
?? null;
|
|
715
719
|
case "currency":
|
|
716
|
-
return listedCurrency(
|
|
720
|
+
return listedCurrency(positionMetrics, ticker, quote) || null;
|
|
717
721
|
case "sector":
|
|
718
722
|
return ticker.metadata.sector || financials?.profile?.sector || null;
|
|
719
723
|
case "industry":
|
|
@@ -165,6 +165,13 @@ const ICONS: Record<IconName, ReactNode> = {
|
|
|
165
165
|
<path d="M7.3 2.9l1.8 1.8" stroke="currentColor" strokeWidth="1.1" />
|
|
166
166
|
</>
|
|
167
167
|
),
|
|
168
|
+
// An envelope: a message written to you.
|
|
169
|
+
mail: (
|
|
170
|
+
<>
|
|
171
|
+
<rect x="1.5" y="2.75" width="9" height="6.5" rx="1.1" stroke="currentColor" strokeWidth="1.1" />
|
|
172
|
+
<path d="M2 3.4 6 6.5l4-3.1" stroke="currentColor" strokeWidth="1.1" strokeLinecap="round" strokeLinejoin="round" />
|
|
173
|
+
</>
|
|
174
|
+
),
|
|
168
175
|
restore: (
|
|
169
176
|
<>
|
|
170
177
|
<rect x="2" y="4" width="6" height="6" rx="0.8" stroke="currentColor" strokeWidth="1.1" />
|
|
@@ -173,7 +180,7 @@ const ICONS: Record<IconName, ReactNode> = {
|
|
|
173
180
|
),
|
|
174
181
|
};
|
|
175
182
|
|
|
176
|
-
export function WebIcon({ name, size = 12, color }: IconProps) {
|
|
183
|
+
export function WebIcon({ name, size = 12, color, fullHeight }: IconProps) {
|
|
177
184
|
return (
|
|
178
185
|
<svg
|
|
179
186
|
viewBox="0 0 12 12"
|
|
@@ -182,7 +189,8 @@ export function WebIcon({ name, size = 12, color }: IconProps) {
|
|
|
182
189
|
fill="none"
|
|
183
190
|
aria-hidden="true"
|
|
184
191
|
data-gloom-icon={name}
|
|
185
|
-
|
|
192
|
+
// The viewBox keeps its size and sits in the middle of the taller box (preserveAspectRatio's default).
|
|
193
|
+
style={{ flex: "none", display: "block", color, ...(fullHeight ? { height: "100%" } : {}) }}
|
|
186
194
|
>
|
|
187
195
|
{ICONS[name]}
|
|
188
196
|
</svg>
|
|
@@ -352,16 +352,14 @@ export class DesktopBackend {
|
|
|
352
352
|
execPath: process.execPath,
|
|
353
353
|
argv: process.argv,
|
|
354
354
|
});
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
this.teardownServices();
|
|
364
|
-
Utils.quit();
|
|
355
|
+
scheduleDesktopRelaunch().then(
|
|
356
|
+
// As a quit does: an app whose windows are gone ends at once on Windows.
|
|
357
|
+
() => this.quit(),
|
|
358
|
+
(error: unknown) => {
|
|
359
|
+
this.restartInProgress = false;
|
|
360
|
+
console.error("[desktop-recovery] failed to schedule restart", summarizeError(error));
|
|
361
|
+
},
|
|
362
|
+
);
|
|
365
363
|
}
|
|
366
364
|
|
|
367
365
|
private syncConfigAccessors(): void {
|
|
@@ -566,6 +564,7 @@ export class DesktopBackend {
|
|
|
566
564
|
case "host.exit":
|
|
567
565
|
case "host.windowControl":
|
|
568
566
|
case "host.windowFullscreen":
|
|
567
|
+
case "host.startWindowDrag":
|
|
569
568
|
case "host.openExternal":
|
|
570
569
|
case "host.copyText":
|
|
571
570
|
case "host.focusWindow":
|
|
@@ -18,6 +18,8 @@ import type { DesktopBackend } from "./backend";
|
|
|
18
18
|
|
|
19
19
|
const INITIAL_DOCK_SUPPRESSION_MS = 800;
|
|
20
20
|
const WINDOW_CONTROL_DOCK_SUPPRESSION_MS = 5_000;
|
|
21
|
+
/** How long after the main window has its data the popped-out windows of the last session open. */
|
|
22
|
+
const LAUNCH_WINDOWS_DELAY_MS = 300;
|
|
21
23
|
|
|
22
24
|
export class DesktopDetachedWindowManager {
|
|
23
25
|
private readonly windows = new Map<string, BrowserWindow>();
|
|
@@ -27,9 +29,29 @@ export class DesktopDetachedWindowManager {
|
|
|
27
29
|
private readonly pendingMoveFlush = new Set<string>();
|
|
28
30
|
private readonly suppressDockUntil = new Map<string, number>();
|
|
29
31
|
private readonly windowControlDockSuppressed = new Set<string>();
|
|
32
|
+
/**
|
|
33
|
+
* At launch, popped-out windows wait for the main window. Opening a window
|
|
34
|
+
* holds this process until the system has made it (on Windows, its
|
|
35
|
+
* browser too), and the main window's `init` used to wait behind each one.
|
|
36
|
+
*/
|
|
37
|
+
private windowsAllowed = false;
|
|
38
|
+
private windowsTimer: ReturnType<typeof setTimeout> | null = null;
|
|
30
39
|
|
|
31
40
|
constructor(private readonly backend: DesktopBackend) {}
|
|
32
41
|
|
|
42
|
+
/**
|
|
43
|
+
* The main window has its data: the popped-out windows open shortly
|
|
44
|
+
* after, once it has had a moment to draw and ask for what it shows.
|
|
45
|
+
*/
|
|
46
|
+
openAfterMainWindow(): void {
|
|
47
|
+
if (this.windowsAllowed || this.windowsTimer) return;
|
|
48
|
+
this.windowsTimer = setTimeout(() => {
|
|
49
|
+
this.windowsTimer = null;
|
|
50
|
+
this.windowsAllowed = true;
|
|
51
|
+
this.reconcile();
|
|
52
|
+
}, LAUNCH_WINDOWS_DELAY_MS);
|
|
53
|
+
}
|
|
54
|
+
|
|
33
55
|
focusDetachedPane(instanceId: string): boolean {
|
|
34
56
|
return this.focusWindowForRpcKey(detachedRpcKey(instanceId));
|
|
35
57
|
}
|
|
@@ -88,7 +110,7 @@ export class DesktopDetachedWindowManager {
|
|
|
88
110
|
for (const [instanceId, entry] of desiredEntries) {
|
|
89
111
|
const existingWindow = this.windows.get(instanceId);
|
|
90
112
|
if (!existingWindow) {
|
|
91
|
-
this.createWindow(instanceId, entry);
|
|
113
|
+
if (this.windowsAllowed) this.createWindow(instanceId, entry);
|
|
92
114
|
continue;
|
|
93
115
|
}
|
|
94
116
|
|
|
@@ -10,6 +10,7 @@ import { safeExternalUrl } from "../../../../utils/external-url";
|
|
|
10
10
|
import { getContextMenuRequestId, normalizeContextMenuItems } from "../context-menu/normalize";
|
|
11
11
|
import { MAIN_WINDOW_RPC_KEY } from "../window/focus";
|
|
12
12
|
import { applyDesktopWindowControl } from "../window/controls";
|
|
13
|
+
import { startSystemWindowMove } from "../window/windows-native";
|
|
13
14
|
import { saveTextFileToDownloads } from "../../../../utils/save-text-file";
|
|
14
15
|
import { setAppBadge } from "./app-badge";
|
|
15
16
|
import { desktopBootFramePath, saveDesktopBootFrame } from "../window/boot-frame";
|
|
@@ -111,6 +112,13 @@ export async function handleDesktopHostRequest(
|
|
|
111
112
|
}
|
|
112
113
|
case "host.windowFullscreen":
|
|
113
114
|
return isWindowFullscreen(backend, backend.rpcs.getRpcWindowKey(rpc));
|
|
115
|
+
case "host.startWindowDrag": {
|
|
116
|
+
const targetWindow = windowForRpcKey(backend, backend.rpcs.getRpcWindowKey(rpc));
|
|
117
|
+
if (!targetWindow?.ptr || !startSystemWindowMove(targetWindow.ptr)) {
|
|
118
|
+
throw new Error("This window cannot be moved by the system.");
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
114
122
|
case "host.openExternal": {
|
|
115
123
|
if (typeof request.payload.url !== "string") {
|
|
116
124
|
throw new Error("host.openExternal requires a URL.");
|
|
@@ -25,6 +25,7 @@ import { telemetryOptedOut } from "../../../../telemetry/crash-reports";
|
|
|
25
25
|
import { describeNodeOs, readOrCreateInstallId } from "../../../../telemetry/crash-reports-node";
|
|
26
26
|
import type { DesktopBackend, DesktopRpc } from "./backend";
|
|
27
27
|
import { readDesktopViewIds } from "./view-ids";
|
|
28
|
+
import { markStartupComplete } from "../../../../utils/main-thread-stall";
|
|
28
29
|
|
|
29
30
|
interface DesktopWindowTarget {
|
|
30
31
|
kind: "main" | "detached";
|
|
@@ -194,5 +195,12 @@ export async function initializeDesktopBackend(
|
|
|
194
195
|
backend.workspace = createDesktopWorkspace(config, backend.getSessionSnapshot());
|
|
195
196
|
backend.detachedWindows.reconcile();
|
|
196
197
|
}
|
|
197
|
-
|
|
198
|
+
const init = buildInitializationPayload(backend, config, services, windowTarget);
|
|
199
|
+
// The main window has what it needs to draw: this process is up, and the
|
|
200
|
+
// popped-out windows open next.
|
|
201
|
+
if (windowTarget.kind === "main") {
|
|
202
|
+
markStartupComplete();
|
|
203
|
+
backend.detachedWindows.openAfterMainWindow();
|
|
204
|
+
}
|
|
205
|
+
return init;
|
|
198
206
|
}
|