gloomberb 0.11.0 → 0.11.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 +38 -15
- package/README.zh-CN.md +12 -12
- package/package.json +10 -3
- package/src/api-client/auth.ts +14 -10
- package/src/api-client/data.ts +54 -0
- package/src/api-client/index.test.ts +124 -0
- package/src/api-client/index.ts +139 -4
- package/src/api-client/paths.ts +89 -0
- package/src/api-client/request.ts +15 -2
- package/src/api-client/socket-health.test.ts +1 -0
- package/src/api-client/socket.ts +2 -1
- package/src/api-client/types.ts +163 -0
- package/src/app/pane-runtime/index.tsx +4 -1
- package/src/app/pane-runtime/plugin-bindings.test.ts +117 -0
- package/src/app/pane-runtime/plugin-bindings.ts +25 -4
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +25 -14
- package/src/app/runtime/desktop-deeplink.test.ts +16 -0
- package/src/app/runtime/desktop-deeplink.ts +66 -2
- package/src/app/runtime/desktop-menu.ts +3 -0
- package/src/app/runtime/plugin-bindings.ts +30 -28
- package/src/app/runtime/update.ts +8 -6
- package/src/app.tsx +21 -7
- package/src/architecture/import-boundaries.test.ts +5 -1
- package/src/capabilities/factories.ts +4 -0
- package/src/cli/commands/automation.test.ts +9 -0
- package/src/cli/commands/automation.ts +22 -73
- package/src/cli/index.ts +10 -6
- package/src/cli/registry.ts +1 -1
- package/src/components/chart/composite/composite-chart.test.tsx +45 -2
- package/src/components/chart/composite/composite-chart.tsx +16 -12
- package/src/components/chart/composite/format.test.ts +34 -0
- package/src/components/chart/composite/format.ts +33 -10
- package/src/components/chart/composite/scene.ts +0 -21
- package/src/components/chart/composite/tools.ts +49 -0
- package/src/components/chart/core/axis-format.ts +13 -3
- package/src/components/chart/core/index.test.ts +7 -0
- package/src/components/chart/core/renderer.ts +3 -4
- package/src/components/command-bar/commands/direct/index.ts +4 -0
- package/src/components/command-bar/commands/registry.ts +8 -1
- package/src/components/command-bar/layout-items/current-layout.ts +1 -1
- package/src/components/command-bar/pane-templates/workflow.ts +4 -0
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -0
- package/src/components/command-bar/routes/root/shortcuts.test.ts +19 -0
- package/src/components/command-bar/routes/root/shortcuts.ts +3 -1
- package/src/components/command-bar/surface/index.test.tsx +6 -6
- package/src/components/command-bar/surface/index.tsx +4 -1
- package/src/components/command-bar/surface/pane.test.tsx +66 -2
- package/src/components/command-bar/view-model.test.ts +2 -1
- package/src/components/command-bar/workflow/ops.test.ts +54 -0
- package/src/components/command-bar/workflow/ops.ts +1 -1
- package/src/components/company-logo.test.ts +19 -0
- package/src/components/company-logo.tsx +60 -0
- package/src/components/feed-data-table/stack-view.tsx +7 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/detached-pane-shell.tsx +85 -15
- package/src/components/layout/header.tsx +5 -5
- package/src/components/layout/pane/title.ts +31 -3
- package/src/components/layout/shell/active-drag.ts +4 -11
- package/src/components/layout/shell/drag/index.ts +4 -20
- package/src/components/layout/shell/drag/runtime.ts +3 -4
- package/src/components/layout/shell/index.test.tsx +17 -3
- package/src/components/layout/shell/index.tsx +89 -31
- package/src/components/layout/shell/menu.test.ts +37 -1
- package/src/components/layout/shell/menu.ts +14 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +22 -8
- package/src/components/layout/shell/pane/actions.ts +0 -5
- package/src/components/layout/shell/pane/management-shortcuts.ts +9 -4
- package/src/components/layout/shell/shortcuts.ts +6 -3
- package/src/components/layout/shell/ticker-link-menu.test.ts +82 -0
- package/src/components/layout/shell/ticker-link-menu.ts +78 -0
- package/src/components/layout/status-bar.test.tsx +62 -61
- package/src/components/layout/status-bar.tsx +53 -75
- package/src/components/onboarding/account-step/form-panel.tsx +4 -2
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.test.tsx +44 -1
- package/src/components/onboarding/onboarding-wizard.tsx +13 -5
- package/src/components/onboarding/wizard-account.ts +4 -23
- package/src/components/table-view-shared.test.ts +11 -0
- package/src/components/table-view-shared.tsx +26 -0
- package/src/components/ui/data-table/index.tsx +19 -1
- package/src/components/ui/fields.tsx +5 -1
- package/src/core/state/app/layout-reducer.ts +36 -0
- package/src/core/state/app/layout.ts +10 -5
- package/src/core/state/app/state.ts +0 -13
- package/src/core/state/app/types.ts +6 -4
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/config/layout.ts +2 -0
- package/src/data/config/store/index.test.ts +27 -6
- package/src/data/config/store/normalize.ts +3 -5
- package/src/data/plugin-state-store.ts +11 -0
- package/src/data/sqlite/cache.test.ts +10 -0
- package/src/i18n/es.ts +13 -5
- package/src/i18n/ja.ts +13 -5
- package/src/i18n/ko.ts +13 -5
- package/src/i18n/zh-cn.ts +13 -5
- package/src/i18n/zh-tw.ts +13 -5
- package/src/layout-marketplace/api.test.ts +54 -0
- package/src/layout-marketplace/api.ts +65 -0
- package/src/layout-marketplace/gallery-desktop.test.tsx +262 -0
- package/src/layout-marketplace/gallery-desktop.tsx +375 -0
- package/src/layout-marketplace/gallery-terminal.tsx +376 -0
- package/src/layout-marketplace/gallery.test.tsx +84 -0
- package/src/layout-marketplace/gallery.tsx +323 -0
- package/src/layout-marketplace/geometry.test.ts +47 -0
- package/src/layout-marketplace/geometry.ts +80 -0
- package/src/layout-marketplace/mini-workspace.test.tsx +122 -0
- package/src/layout-marketplace/mini-workspace.tsx +428 -0
- package/src/layout-marketplace/model.test.ts +118 -0
- package/src/layout-marketplace/model.ts +171 -0
- package/src/layout-marketplace/name-dialog.tsx +60 -0
- package/src/layout-marketplace/pane-imagery.ts +85 -0
- package/src/layout-marketplace/pane.tsx +20 -0
- package/src/layout-marketplace/payload.test.ts +246 -0
- package/src/layout-marketplace/payload.ts +624 -0
- package/src/layout-marketplace/use-marketplace.ts +63 -0
- package/src/market-data/coordinator/index.test.ts +2 -2
- package/src/market-data/coordinator/index.ts +1 -1
- package/src/market-data/hooks.tsx +0 -41
- package/src/market-data/market/format.test.ts +7 -0
- package/src/market-data/market/freshness.ts +5 -2
- package/src/market-data/quotes/freshness.test.ts +16 -0
- package/src/news/aggregator.test.ts +92 -3
- package/src/news/aggregator.ts +98 -18
- package/src/news/hooks.test.tsx +2 -0
- package/src/news/hooks.ts +27 -1
- package/src/news/news-model.ts +5 -2
- package/src/news/types.ts +7 -0
- package/src/plugins/broker-sync/broker-sync.test.ts +58 -0
- package/src/plugins/broker-sync/normalize.ts +42 -5
- package/src/plugins/builtin/account-management/pane.tsx +5 -5
- package/src/plugins/builtin/ai/index.tsx +18 -2
- package/src/plugins/builtin/ai/screener/pane.test.tsx +47 -4
- package/src/plugins/builtin/ai/screener/pane.tsx +35 -12
- package/src/plugins/builtin/alerts/alert-engine.test.ts +53 -1
- package/src/plugins/builtin/alerts/alert-engine.ts +34 -1
- package/src/plugins/builtin/alerts/index.test.tsx +49 -6
- package/src/plugins/builtin/alerts/index.tsx +11 -6
- package/src/plugins/builtin/alerts/pane.tsx +51 -9
- package/src/plugins/builtin/alerts/quotes.ts +11 -3
- package/src/plugins/builtin/alerts/types.ts +1 -0
- package/src/plugins/builtin/analytics/index.tsx +11 -1
- package/src/plugins/builtin/analytics/pane-model.ts +6 -4
- package/src/plugins/builtin/broker-manager/index.tsx +3 -0
- package/src/plugins/builtin/buildout/model/index.ts +1 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +17 -18
- package/src/plugins/builtin/cds/client.test.ts +83 -0
- package/src/plugins/builtin/cds/client.ts +67 -0
- package/src/plugins/builtin/cds/index.tsx +41 -0
- package/src/plugins/builtin/cds/model.test.ts +232 -0
- package/src/plugins/builtin/cds/model.ts +401 -0
- package/src/plugins/builtin/cds/pane.test.tsx +132 -0
- package/src/plugins/builtin/cds/pane.tsx +358 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +31 -0
- package/src/plugins/builtin/chart-composer/index.tsx +88 -2
- package/src/plugins/builtin/chart-composer/pane-share.test.ts +71 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +103 -15
- package/src/plugins/builtin/chat/content/index.tsx +4 -1
- package/src/plugins/builtin/chat/controller/channels.ts +7 -1
- package/src/plugins/builtin/chat/controller/index.ts +14 -9
- package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
- package/src/plugins/builtin/chat/controller/persistence.ts +0 -3
- package/src/plugins/builtin/chat/controller/realtime.ts +3 -3
- package/src/plugins/builtin/chat/controller/send.ts +1 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +18 -11
- package/src/plugins/builtin/chat/controller/state.ts +1 -3
- package/src/plugins/builtin/chat/controller/storage.ts +0 -2
- package/src/plugins/builtin/chat/controller/utils.ts +4 -6
- package/src/plugins/builtin/chat/controller/view.ts +2 -2
- package/src/plugins/builtin/chat/controller.test.ts +142 -14
- package/src/plugins/builtin/chat/message/desktop.test.tsx +88 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +3 -1
- package/src/plugins/builtin/chat/message/profile-popover.test.tsx +1 -29
- package/src/plugins/builtin/chat/profile-popover.ts +53 -22
- package/src/plugins/builtin/chat/sidebar.test.tsx +81 -0
- package/src/plugins/builtin/chat/sidebar.tsx +63 -0
- package/src/plugins/builtin/chat/test-harness.tsx +29 -1
- package/src/plugins/builtin/cloud/auth-actions.tsx +14 -7
- package/src/plugins/builtin/cloud/auth-commands.ts +21 -51
- package/src/plugins/builtin/cloud/auth-dialog.tsx +296 -0
- package/src/{components/onboarding/account-step/model.test.ts → plugins/builtin/cloud/auth-model.test.ts} +1 -1
- package/src/{components/onboarding/account-step/model.ts → plugins/builtin/cloud/auth-model.ts} +37 -3
- package/src/plugins/builtin/cloud/browser.tsx +9 -0
- package/src/plugins/builtin/cloud/buildout-module.tsx +23 -0
- package/src/plugins/builtin/cloud/device-signin.ts +2 -8
- package/src/plugins/builtin/cloud/index.tsx +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +14 -24
- package/src/plugins/builtin/cloud/upgrade-command.ts +13 -6
- package/src/plugins/builtin/cloud-tweets/pane-share.test.ts +56 -0
- package/src/plugins/builtin/cloud-tweets/pane.tsx +20 -5
- package/src/plugins/builtin/cloud-tweets/registration.ts +50 -6
- package/src/plugins/builtin/composite-plugins.ts +3 -1
- package/src/plugins/builtin/congress-trades/detail.tsx +43 -1
- package/src/plugins/builtin/congress-trades/model.test.ts +85 -0
- package/src/plugins/builtin/congress-trades/model.ts +69 -1
- package/src/plugins/builtin/congress-trades/pane.tsx +51 -2
- package/src/plugins/builtin/connections/index.ts +3 -0
- package/src/plugins/builtin/dividend-yield/client.ts +18 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +7 -6
- package/src/plugins/builtin/earnings/index.tsx +5 -3
- package/src/plugins/builtin/earnings/model.test.ts +19 -1
- package/src/plugins/builtin/earnings/model.ts +17 -15
- package/src/plugins/builtin/econ/calendar-model.ts +0 -5
- package/src/plugins/builtin/econ/index.test.tsx +6 -1
- package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
- package/src/plugins/builtin/help/index.tsx +2 -2
- package/src/plugins/builtin/holders/thirteenf-match.ts +1 -2
- package/src/plugins/builtin/insider/index.tsx +27 -11
- package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
- package/src/plugins/builtin/layout-manager/index.tsx +17 -4
- package/src/plugins/builtin/news/index.tsx +11 -6
- package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +2 -0
- package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +2 -0
- package/src/plugins/builtin/news/wire/breaking/pane.tsx +4 -1
- package/src/plugins/builtin/news/wire/feed-config.test.ts +15 -1
- package/src/plugins/builtin/news/wire/feed-config.ts +25 -5
- package/src/plugins/builtin/news/wire/index.tsx +3 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +7 -2
- package/src/plugins/builtin/news/wire/news/footer.ts +34 -1
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +4 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +8 -2
- package/src/plugins/builtin/news/wire/rss/source.test.ts +12 -0
- package/src/plugins/builtin/news/wire/rss/source.ts +1 -1
- package/src/plugins/builtin/notes/files.ts +80 -0
- package/src/plugins/builtin/notes/index.tsx +3 -0
- package/src/plugins/builtin/notes/sync.test.ts +49 -0
- package/src/plugins/builtin/notes/sync.ts +98 -0
- package/src/plugins/builtin/options/index.tsx +1 -0
- package/src/plugins/builtin/options/view.test.tsx +25 -3
- package/src/plugins/builtin/options/view.tsx +1 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +22 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +12 -7
- package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
- package/src/plugins/builtin/portfolio-list/column-values.ts +15 -4
- package/src/plugins/builtin/portfolio-list/header.tsx +13 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +8 -0
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
- package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +1 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +6 -1
- package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +35 -21
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -12
- package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +329 -0
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +582 -0
- package/src/plugins/builtin/research/index.test.ts +14 -0
- package/src/plugins/builtin/research/index.tsx +29 -0
- package/src/plugins/builtin/sec/index.tsx +24 -9
- package/src/plugins/builtin/sectors/index.tsx +14 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +18 -11
- package/src/plugins/builtin/shared/public-pane.test.ts +12 -0
- package/src/plugins/builtin/shared/public-pane.ts +9 -0
- package/src/plugins/builtin/shared/public-share.ts +23 -0
- package/src/plugins/builtin/shared/ticker-source.ts +32 -0
- package/src/plugins/builtin/shared/ticker-surface.test.ts +27 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +17 -0
- package/src/plugins/builtin/substack/api/cache.ts +7 -3
- package/src/plugins/builtin/substack/api/loaders.ts +36 -12
- package/src/plugins/builtin/substack/api/types.ts +2 -0
- package/src/plugins/builtin/substack/pane-state.ts +1 -1
- package/src/plugins/builtin/substack/pane.tsx +55 -12
- package/src/plugins/builtin/thirteenf/api.test.ts +1 -1
- package/src/plugins/builtin/thirteenf/api.ts +40 -18
- package/src/plugins/builtin/thirteenf/data.ts +15 -7
- package/src/plugins/builtin/thirteenf/pane.tsx +45 -10
- package/src/plugins/builtin/ticker-detail/index.tsx +10 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +12 -4
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -2
- package/src/plugins/builtin/ticker-detail/settings.ts +12 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +2 -0
- package/src/plugins/builtin/world-indices/index.tsx +2 -0
- package/src/plugins/catalog-browser.test.ts +78 -0
- package/src/plugins/catalog-browser.ts +136 -0
- package/src/plugins/gridlock-notification.ts +2 -1
- package/src/plugins/ibkr/flex/index.test.ts +19 -0
- package/src/plugins/ibkr/flex/index.ts +1 -1
- package/src/plugins/pane-manager/gridlock.ts +87 -3
- package/src/plugins/pane-manager.test.ts +30 -0
- package/src/plugins/pane-manager.ts +6 -1
- package/src/plugins/prediction-markets/chart.tsx +2 -2
- package/src/plugins/prediction-markets/controller/catalog.ts +77 -2
- package/src/plugins/prediction-markets/controller/data.ts +7 -1
- package/src/plugins/prediction-markets/controller/index.ts +9 -2
- package/src/plugins/prediction-markets/controller/keyboard.ts +10 -14
- package/src/plugins/prediction-markets/detail/overview.tsx +21 -0
- package/src/plugins/prediction-markets/detail/pane.tsx +43 -34
- package/src/plugins/prediction-markets/detail/trades.tsx +34 -9
- package/src/plugins/prediction-markets/detail.test.tsx +29 -2
- package/src/plugins/prediction-markets/index.tsx +8 -4
- package/src/plugins/prediction-markets/navigation.ts +0 -1
- package/src/plugins/prediction-markets/pane.test.tsx +77 -0
- package/src/plugins/prediction-markets/pane.tsx +12 -0
- package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
- package/src/plugins/prediction-markets/services/kalshi/adapter.ts +49 -9
- package/src/plugins/prediction-markets/services/polymarket/adapter.ts +31 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
- package/src/plugins/registry/index.ts +2 -0
- package/src/plugins/ticker-navigation.test.ts +24 -0
- package/src/plugins/ticker-navigation.ts +40 -0
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/app-services.ts +61 -0
- package/src/renderers/browser/cloud-transport.test.ts +68 -0
- package/src/renderers/browser/cloud-transport.ts +48 -0
- package/src/renderers/browser/config-host.ts +58 -0
- package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
- package/src/renderers/browser/deeplink-bridge.ts +22 -0
- package/src/renderers/browser/error-boundary.tsx +23 -0
- package/src/renderers/browser/main.tsx +64 -0
- package/src/renderers/browser/persistence.ts +74 -0
- package/src/renderers/browser/storage.test.ts +66 -0
- package/src/renderers/browser/storage.ts +50 -0
- package/src/renderers/browser/ticker-repository.ts +59 -0
- package/src/renderers/browser/ui-host.tsx +40 -0
- package/src/renderers/cloudflare/worker.test.ts +136 -0
- package/src/renderers/cloudflare/worker.ts +116 -0
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +7 -1
- package/src/renderers/electrobun/view/app-services.ts +14 -3
- package/src/renderers/electrobun/view/build-assets.ts +12 -10
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/data-table/index.tsx +10 -1
- package/src/renderers/electrobun/view/desktop/controls.tsx +2 -0
- package/src/renderers/electrobun/view/dom-ui-host.tsx +143 -0
- package/src/renderers/electrobun/view/host/box.test.tsx +33 -0
- package/src/renderers/electrobun/view/host/box.tsx +11 -6
- package/src/renderers/electrobun/view/host/chart-surface.tsx +7 -1
- package/src/renderers/electrobun/view/host/input.tsx +11 -7
- package/src/renderers/electrobun/view/host/native.ts +0 -5
- package/src/renderers/electrobun/view/main.tsx +2 -0
- package/src/renderers/electrobun/view/remote/persistence.ts +5 -0
- package/src/renderers/electrobun/view/styles.css +107 -7
- package/src/renderers/electrobun/view/toast-host.test.tsx +78 -0
- package/src/renderers/electrobun/view/toast-host.tsx +69 -43
- package/src/renderers/electrobun/view/ui-host.tsx +6 -137
- package/src/renderers/opentui/start.tsx +2 -0
- package/src/renderers/share/layout-view.test.tsx +58 -0
- package/src/renderers/share/layout-view.tsx +133 -0
- package/src/renderers/share/main.tsx +102 -0
- package/src/renderers/share/styles.css +54 -0
- package/src/renderers/share/view.test.tsx +100 -0
- package/src/renderers/share/view.tsx +150 -0
- package/src/shares/api.test.ts +105 -0
- package/src/shares/api.ts +127 -0
- package/src/shares/chart-snapshot.test.ts +34 -0
- package/src/shares/chart-snapshot.ts +28 -0
- package/src/shares/location.test.ts +19 -0
- package/src/shares/location.ts +11 -0
- package/src/shares/pane.test.ts +110 -0
- package/src/shares/pane.ts +54 -0
- package/src/shares/payload.ts +177 -0
- package/src/sources/gloomberb-cloud/index.test.ts +61 -1
- package/src/sources/gloomberb-cloud/index.ts +86 -14
- package/src/sources/gloomberb-cloud/normalizers.test.ts +34 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +5 -5
- package/src/sources/provider-router/history.test.ts +2 -2
- package/src/sources/provider-router/news.test.ts +34 -0
- package/src/sources/provider-router/news.ts +15 -1
- package/src/sources/sec-edgar.test.ts +59 -0
- package/src/sources/sec-edgar.ts +63 -23
- package/src/sources/yahoo-finance/mappers.ts +3 -0
- package/src/sources/yahoo-finance/snapshots.ts +10 -2
- package/src/sources/yahoo-finance/symbols.test.ts +10 -0
- package/src/sources/yahoo-finance/symbols.ts +13 -8
- package/src/sources/yahoo-finance.test.ts +1 -1
- package/src/sources/yahoo-finance.ts +6 -2
- package/src/state/app/bootstrap.ts +6 -7
- package/src/state/app/context/index.test.ts +41 -1
- package/src/state/app/context/index.test.tsx +34 -16
- package/src/state/selectors-ui.ts +0 -8
- package/src/sync/baseline.ts +32 -0
- package/src/sync/controller.test.ts +124 -1
- package/src/sync/controller.ts +54 -12
- package/src/sync/core-contributors.test.ts +44 -0
- package/src/sync/core-contributors.ts +59 -10
- package/src/sync/react.ts +22 -2
- package/src/sync/types.ts +12 -0
- package/src/theme/font-scale.ts +0 -11
- package/src/tickers/search/index.test.ts +27 -0
- package/src/tickers/search/upsert.ts +10 -0
- package/src/types/capability-route-source.ts +2 -1
- package/src/types/config.ts +35 -5
- package/src/types/desktop-menu.ts +1 -0
- package/src/types/plugin.ts +36 -0
- package/src/ui/context-menu.tsx +1 -1
- package/src/ui/host.tsx +4 -0
- package/src/ui/toast.tsx +2 -0
- package/src/utils/back-navigation.ts +0 -4
- package/src/utils/price-history.ts +11 -10
- package/src/utils/sec.test.ts +6 -0
- package/src/utils/sec.ts +1 -1
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
useRef,
|
|
6
6
|
useState,
|
|
7
7
|
} from "react";
|
|
8
|
-
import { apiClient } from "../../api-client";
|
|
8
|
+
import { apiClient, type CloudPricing } from "../../api-client";
|
|
9
9
|
import type { AppBrokerImportRuntime } from "../../app/runtime/broker-import";
|
|
10
10
|
import type { SyncBrokerInstanceResult } from "../../brokers/sync-broker-instance";
|
|
11
11
|
import { saveConfigImmediately } from "../../state/config-save-scheduler";
|
|
@@ -28,6 +28,7 @@ import { t, tf } from "../../i18n";
|
|
|
28
28
|
import { useAppLanguage } from "../../i18n/react";
|
|
29
29
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
30
30
|
import { chatController } from "../../plugins/builtin/chat/controller";
|
|
31
|
+
import { formatCloudMonthlyPrice } from "../../plugins/builtin/account-management/model";
|
|
31
32
|
import { useCloudUpgradeAction } from "../../plugins/builtin/shared/cloud-upgrade";
|
|
32
33
|
import { usePlanAccess } from "../../plugins/builtin/shared/plan-access";
|
|
33
34
|
import type { ListViewItem } from "../ui";
|
|
@@ -42,7 +43,7 @@ import {
|
|
|
42
43
|
OnboardingTitle,
|
|
43
44
|
type OnboardingSectionId,
|
|
44
45
|
} from "./onboarding-frame";
|
|
45
|
-
import { ACCOUNT_CHOICE_IDS } from "
|
|
46
|
+
import { ACCOUNT_CHOICE_IDS } from "../../plugins/builtin/cloud/auth-model";
|
|
46
47
|
import { useOnboardingAccount } from "./wizard-account";
|
|
47
48
|
import { useOnboardingBrokerSync } from "./wizard-broker-sync";
|
|
48
49
|
import {
|
|
@@ -71,6 +72,7 @@ export function OnboardingWizard({ pluginRegistry, importBrokerPositions, onComp
|
|
|
71
72
|
const stage = progress.stage;
|
|
72
73
|
const [persistenceError, setPersistenceError] = useState<string | null>(null);
|
|
73
74
|
const [isFinishing, setIsFinishing] = useState(false);
|
|
75
|
+
const [pricing, setPricing] = useState<CloudPricing | null>(null);
|
|
74
76
|
|
|
75
77
|
const [portfolioSub, setPortfolioSub] = useState<PortfolioSub>("choose");
|
|
76
78
|
const [portfolioOptionIdx, setPortfolioOptionIdx] = useState(0);
|
|
@@ -254,6 +256,11 @@ export function OnboardingWizard({ pluginRegistry, importBrokerPositions, onComp
|
|
|
254
256
|
},
|
|
255
257
|
), [pluginRegistry.events, saveProgressInBackground, stateRef]);
|
|
256
258
|
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
if (stage !== "upgrade" || pricing) return;
|
|
261
|
+
void apiClient.getCloudPricing().then(setPricing).catch(() => {});
|
|
262
|
+
}, [pricing, stage]);
|
|
263
|
+
|
|
257
264
|
const appActive = useAppActive();
|
|
258
265
|
const planAccess = usePlanAccess();
|
|
259
266
|
const openUpgrade = useCloudUpgradeAction();
|
|
@@ -859,6 +866,7 @@ export function OnboardingWizard({ pluginRegistry, importBrokerPositions, onComp
|
|
|
859
866
|
|
|
860
867
|
if (stage === "upgrade") {
|
|
861
868
|
const primaryLabel = planAccess.hasProAccess ? t("Continue with Pro") : t("Start 7-day free trial");
|
|
869
|
+
const monthlyPrice = formatCloudMonthlyPrice(pricing);
|
|
862
870
|
return (
|
|
863
871
|
<OnboardingModal width={66} height={17}>
|
|
864
872
|
<OnboardingHeader
|
|
@@ -871,10 +879,10 @@ export function OnboardingWizard({ pluginRegistry, importBrokerPositions, onComp
|
|
|
871
879
|
/>
|
|
872
880
|
<OnboardingTitle
|
|
873
881
|
step={desktop ? undefined : t("GLOOM CLOUD PRO")}
|
|
874
|
-
title={planAccess.hasProAccess ? t("Pro is active") :
|
|
875
|
-
titlePrefix={!planAccess.hasProAccess ? (
|
|
882
|
+
title={planAccess.hasProAccess ? t("Pro is active") : monthlyPrice.price}
|
|
883
|
+
titlePrefix={!planAccess.hasProAccess && monthlyPrice.anchor ? (
|
|
876
884
|
<Text fg={colors.textMuted} attributes={TextAttributes.STRIKETHROUGH}>
|
|
877
|
-
{
|
|
885
|
+
{monthlyPrice.anchor}
|
|
878
886
|
</Text>
|
|
879
887
|
) : undefined}
|
|
880
888
|
description={planAccess.hasProAccess
|
|
@@ -1,19 +1,17 @@
|
|
|
1
1
|
import { useCallback, useRef, useState, type Dispatch, type SetStateAction } from "react";
|
|
2
2
|
import { apiClient } from "../../api-client";
|
|
3
|
-
import { chatController } from "../../plugins/builtin/chat/controller";
|
|
4
3
|
import { debugLog } from "../../utils/debug-log";
|
|
5
4
|
import {
|
|
6
5
|
advanceAccountField,
|
|
7
6
|
classifyAccountError,
|
|
8
|
-
|
|
9
|
-
isUsernameConflictError,
|
|
7
|
+
performEmailAuth,
|
|
10
8
|
validateAccountEmail,
|
|
11
9
|
validateAccountPassword,
|
|
12
10
|
type AccountMode,
|
|
13
11
|
type AccountOutcome,
|
|
14
12
|
type AccountSub,
|
|
15
13
|
type AccountSubmitError,
|
|
16
|
-
} from "
|
|
14
|
+
} from "../../plugins/builtin/cloud/auth-model";
|
|
17
15
|
|
|
18
16
|
const onboardingLog = debugLog.createLogger("onboarding");
|
|
19
17
|
|
|
@@ -175,24 +173,7 @@ export function useOnboardingAccount({
|
|
|
175
173
|
|
|
176
174
|
void (async () => {
|
|
177
175
|
try {
|
|
178
|
-
|
|
179
|
-
try {
|
|
180
|
-
const username = deriveUsernameFromEmail(email);
|
|
181
|
-
await apiClient.signUp(email, username, username, password);
|
|
182
|
-
} catch (error) {
|
|
183
|
-
if (!isUsernameConflictError(error)) throw error;
|
|
184
|
-
const retryUsername = deriveUsernameFromEmail(email, 1);
|
|
185
|
-
await apiClient.signUp(email, retryUsername, retryUsername, password);
|
|
186
|
-
}
|
|
187
|
-
await apiClient.sendVerification().catch(() => {});
|
|
188
|
-
} else {
|
|
189
|
-
await apiClient.signIn(email, password);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
// Hands the freshly captured session cookie to the chat controller so it
|
|
193
|
-
// reaches plugin persistence instead of living only in this process.
|
|
194
|
-
chatController.clearSession();
|
|
195
|
-
await chatController.refreshSession().catch(() => {});
|
|
176
|
+
await performEmailAuth(mode, email, password);
|
|
196
177
|
|
|
197
178
|
if (attemptRef.current !== attemptId) return;
|
|
198
179
|
onboardingLog.info("Onboarding account step completed", { mode });
|
|
@@ -235,7 +216,7 @@ export function useOnboardingAccount({
|
|
|
235
216
|
}, [accountEmail, accountFieldIdx, accountPassword, accountSub, setEditingField, submitAccount]);
|
|
236
217
|
|
|
237
218
|
const syncExistingAccountSession = useCallback(() => {
|
|
238
|
-
if (accountSub !== "choose" || !apiClient.
|
|
219
|
+
if (accountSub !== "choose" || !apiClient.isSignedIn()) return;
|
|
239
220
|
const user = apiClient.getCurrentUser();
|
|
240
221
|
setAccountOutcome((current) => current ?? {
|
|
241
222
|
mode: "login",
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { isTableScrollNearEnd } from "./table-view-shared";
|
|
3
|
+
|
|
4
|
+
describe("isTableScrollNearEnd", () => {
|
|
5
|
+
test("is false until the remaining scroll is within the threshold", () => {
|
|
6
|
+
const scrollBox = { scrollTop: 0, scrollHeight: 40, viewport: { height: 20 } };
|
|
7
|
+
expect(isTableScrollNearEnd(scrollBox, 8)).toBe(false);
|
|
8
|
+
scrollBox.scrollTop = 12;
|
|
9
|
+
expect(isTableScrollNearEnd(scrollBox, 8)).toBe(true);
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -140,6 +140,32 @@ export function useTableViewState({
|
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
+
export function isTableScrollNearEnd(
|
|
144
|
+
scrollBox: {
|
|
145
|
+
scrollTop: number;
|
|
146
|
+
scrollHeight: number;
|
|
147
|
+
viewport?: { height: number } | null;
|
|
148
|
+
} | null,
|
|
149
|
+
threshold = 8,
|
|
150
|
+
): boolean {
|
|
151
|
+
if (!scrollBox?.viewport) return false;
|
|
152
|
+
const viewportHeight = Math.max(1, scrollBox.viewport.height);
|
|
153
|
+
return scrollBox.scrollTop + viewportHeight >= scrollBox.scrollHeight - threshold;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export function useTableLoadMore(
|
|
157
|
+
scrollRef: RefObject<ScrollBoxRenderable | null>,
|
|
158
|
+
canLoadMore: boolean,
|
|
159
|
+
loadMore: () => void,
|
|
160
|
+
threshold = 8,
|
|
161
|
+
) {
|
|
162
|
+
return useCallback(() => {
|
|
163
|
+
if (!canLoadMore) return;
|
|
164
|
+
if (!isTableScrollNearEnd(scrollRef.current, threshold)) return;
|
|
165
|
+
loadMore();
|
|
166
|
+
}, [canLoadMore, loadMore, scrollRef, threshold]);
|
|
167
|
+
}
|
|
168
|
+
|
|
143
169
|
export function useTableBodyScrollActivity({
|
|
144
170
|
onBodyScrollActivity,
|
|
145
171
|
syncHeaderScroll,
|
|
@@ -6,7 +6,7 @@ import type {
|
|
|
6
6
|
DataTableProps,
|
|
7
7
|
} from "./types";
|
|
8
8
|
import { useRemoteUiNode } from "../../../remote/semantic-tree";
|
|
9
|
-
import { resolveRemoteItemIndex } from "../../../remote/semantic-helpers";
|
|
9
|
+
import { remoteNumberValue, resolveRemoteItemIndex } from "../../../remote/semantic-helpers";
|
|
10
10
|
|
|
11
11
|
export type {
|
|
12
12
|
DataTableCell,
|
|
@@ -46,6 +46,24 @@ export function DataTable<T, C extends DataTableColumn = DataTableColumn>(
|
|
|
46
46
|
props.onHeaderClick(columnId);
|
|
47
47
|
}
|
|
48
48
|
},
|
|
49
|
+
scrollTo: (input) => {
|
|
50
|
+
const box = props.scrollRef.current;
|
|
51
|
+
if (!box) return;
|
|
52
|
+
box.scrollTo(Math.max(0, Math.round(remoteNumberValue(input, ["top", "index"]))));
|
|
53
|
+
props.onBodyScrollActivity();
|
|
54
|
+
},
|
|
55
|
+
scrollBy: (input) => {
|
|
56
|
+
const box = props.scrollRef.current;
|
|
57
|
+
if (!box) return;
|
|
58
|
+
const direction = input && typeof input === "object"
|
|
59
|
+
? (input as { direction?: unknown }).direction
|
|
60
|
+
: undefined;
|
|
61
|
+
const delta = direction === "up"
|
|
62
|
+
? remoteNumberValue(input, ["delta"], -1)
|
|
63
|
+
: remoteNumberValue(input, ["delta"], 1);
|
|
64
|
+
box.scrollTo(Math.max(0, Math.round((box.scrollTop ?? 0) + delta)));
|
|
65
|
+
props.onBodyScrollActivity();
|
|
66
|
+
},
|
|
49
67
|
},
|
|
50
68
|
metadata: {
|
|
51
69
|
sortColumnId: props.sortColumnId,
|
|
@@ -16,7 +16,9 @@ export interface TextFieldProps {
|
|
|
16
16
|
onSubmit?: (value: string) => void;
|
|
17
17
|
onBlur?: (value: string) => void;
|
|
18
18
|
hint?: string;
|
|
19
|
-
type?: "text" | "password" | "date";
|
|
19
|
+
type?: "text" | "password" | "date" | "email";
|
|
20
|
+
/** DOM autofill hint, e.g. "email" or "current-password"; terminal hosts ignore it. */
|
|
21
|
+
autoComplete?: string;
|
|
20
22
|
variant?: "default" | "plain";
|
|
21
23
|
backgroundColor?: string;
|
|
22
24
|
textColor?: string;
|
|
@@ -49,6 +51,7 @@ export function TextField({
|
|
|
49
51
|
onBlur,
|
|
50
52
|
hint,
|
|
51
53
|
type = "text",
|
|
54
|
+
autoComplete,
|
|
52
55
|
variant = "default",
|
|
53
56
|
backgroundColor = colors.bg,
|
|
54
57
|
textColor = colors.text,
|
|
@@ -87,6 +90,7 @@ export function TextField({
|
|
|
87
90
|
onBlur={onBlur}
|
|
88
91
|
hint={hint}
|
|
89
92
|
type={type}
|
|
93
|
+
autoComplete={autoComplete}
|
|
90
94
|
variant={variant}
|
|
91
95
|
backgroundColor={backgroundColor}
|
|
92
96
|
textColor={textColor}
|
|
@@ -14,6 +14,15 @@ import type { AppAction, AppState } from "./types";
|
|
|
14
14
|
|
|
15
15
|
const MAX_LAYOUT_HISTORY = 50;
|
|
16
16
|
|
|
17
|
+
function availableLayoutName(name: string, layouts: SavedLayout[]): string {
|
|
18
|
+
const base = name.trim() || "Community Layout";
|
|
19
|
+
const existing = new Set(layouts.map((layout) => layout.name.toLowerCase()));
|
|
20
|
+
if (!existing.has(base.toLowerCase())) return base;
|
|
21
|
+
let suffix = 2;
|
|
22
|
+
while (existing.has(`${base} (${suffix})`.toLowerCase())) suffix += 1;
|
|
23
|
+
return `${base} (${suffix})`;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
export function reduceLayoutAction(state: AppState, action: AppAction): AppState | undefined {
|
|
18
27
|
switch (action.type) {
|
|
19
28
|
case "PUSH_LAYOUT_HISTORY": {
|
|
@@ -153,6 +162,33 @@ export function reduceLayoutAction(state: AppState, action: AppAction): AppState
|
|
|
153
162
|
});
|
|
154
163
|
}
|
|
155
164
|
|
|
165
|
+
case "INSTALL_LAYOUT_COPY": {
|
|
166
|
+
const currentConfig = syncConfigActiveLayoutState(
|
|
167
|
+
state.config,
|
|
168
|
+
state.paneState,
|
|
169
|
+
state.focusedPaneId,
|
|
170
|
+
state.activePanel,
|
|
171
|
+
);
|
|
172
|
+
const installed: SavedLayout = {
|
|
173
|
+
name: availableLayoutName(action.name, currentConfig.layouts),
|
|
174
|
+
layout: cloneLayout(action.layout),
|
|
175
|
+
paneState: clonePaneStateMap(action.paneState),
|
|
176
|
+
};
|
|
177
|
+
const layouts = [...currentConfig.layouts, installed];
|
|
178
|
+
return withFocusedPane({
|
|
179
|
+
...state,
|
|
180
|
+
layoutHistory: setHistoryForIndex(state.layoutHistory, layouts.length - 1, { past: [], future: [] }),
|
|
181
|
+
}, {
|
|
182
|
+
...currentConfig,
|
|
183
|
+
layout: cloneLayout(installed.layout),
|
|
184
|
+
layouts,
|
|
185
|
+
activeLayoutIndex: layouts.length - 1,
|
|
186
|
+
}, {
|
|
187
|
+
paneState: clonePaneStateMap(installed.paneState ?? {}),
|
|
188
|
+
focusedPaneId: null,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
156
192
|
case "DELETE_LAYOUT": {
|
|
157
193
|
if (state.config.layouts.length <= 1) return state;
|
|
158
194
|
const currentConfig = syncConfigActiveLayoutState(
|
|
@@ -118,15 +118,17 @@ function getPaneState(state: Pick<AppState, "paneState">, paneId: string): PaneR
|
|
|
118
118
|
return state.paneState[paneId] ?? {};
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Any pane that publishes `cursorSymbol` in its pane state is a ticker source; every other pane
|
|
123
|
+
* resolves through its binding, so follow chains keep working across source types.
|
|
124
|
+
*/
|
|
121
125
|
export function resolveTickerForPane(state: AppState, paneId: string, seen = new Set<string>()): string | null {
|
|
122
126
|
if (seen.has(paneId)) return null;
|
|
123
127
|
seen.add(paneId);
|
|
124
128
|
const instance = findPaneInstance(state.config.layout, paneId);
|
|
125
129
|
if (!instance) return null;
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
return typeof paneState.cursorSymbol === "string" ? paneState.cursorSymbol : null;
|
|
129
|
-
}
|
|
130
|
+
const cursorSymbol = getPaneState(state, paneId).cursorSymbol;
|
|
131
|
+
if (typeof cursorSymbol === "string" && cursorSymbol.trim()) return cursorSymbol;
|
|
130
132
|
return resolveTickerFromBinding(state, instance.binding, seen);
|
|
131
133
|
}
|
|
132
134
|
|
|
@@ -388,7 +390,10 @@ export function withFocusedPane(
|
|
|
388
390
|
activePanel?: "left" | "right";
|
|
389
391
|
} = {},
|
|
390
392
|
): AppState {
|
|
391
|
-
const normalizedLayout = normalizePaneLayout(config.layout
|
|
393
|
+
const normalizedLayout = normalizePaneLayout(config.layout, {
|
|
394
|
+
// Keep a follower alive on its last symbol when its source pane is gone.
|
|
395
|
+
resolveOrphanSymbol: (instanceId) => resolveTickerForPane(state, instanceId),
|
|
396
|
+
});
|
|
392
397
|
const nextConfig = normalizedLayout === config.layout
|
|
393
398
|
? config
|
|
394
399
|
: {
|
|
@@ -173,17 +173,6 @@ export function appReducer(state: AppState, action: AppAction): AppState {
|
|
|
173
173
|
case "TOGGLE_STATUS_BAR":
|
|
174
174
|
return { ...state, statusBarVisible: !state.statusBarVisible };
|
|
175
175
|
|
|
176
|
-
case "SHOW_GRIDLOCK_TIP":
|
|
177
|
-
return {
|
|
178
|
-
...state,
|
|
179
|
-
gridlockTipVisible: true,
|
|
180
|
-
gridlockTipSequence: state.gridlockTipSequence + 1,
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
case "DISMISS_GRIDLOCK_TIP":
|
|
184
|
-
if (!state.gridlockTipVisible) return state;
|
|
185
|
-
return { ...state, gridlockTipVisible: false };
|
|
186
|
-
|
|
187
176
|
case "SET_THEME":
|
|
188
177
|
if (state.config.theme === action.theme && state.themePreview == null) return state;
|
|
189
178
|
return { ...state, themePreview: null, config: { ...state.config, theme: action.theme } };
|
|
@@ -360,8 +349,6 @@ export function createInitialState(config: AppConfig, sessionSnapshot: AppSessio
|
|
|
360
349
|
refreshing: new Set(),
|
|
361
350
|
initialized: false,
|
|
362
351
|
statusBarVisible: sessionSnapshot?.statusBarVisible !== false,
|
|
363
|
-
gridlockTipVisible: false,
|
|
364
|
-
gridlockTipSequence: 0,
|
|
365
352
|
inputCaptured: false,
|
|
366
353
|
updateAvailable: null,
|
|
367
354
|
updateProgress: null,
|
|
@@ -58,8 +58,6 @@ export interface AppState {
|
|
|
58
58
|
refreshing: Set<string>;
|
|
59
59
|
initialized: boolean;
|
|
60
60
|
statusBarVisible: boolean;
|
|
61
|
-
gridlockTipVisible: boolean;
|
|
62
|
-
gridlockTipSequence: number;
|
|
63
61
|
inputCaptured: boolean;
|
|
64
62
|
updateAvailable: ReleaseInfo | null;
|
|
65
63
|
updateProgress: UpdateProgress | null;
|
|
@@ -95,8 +93,6 @@ export type AppAction =
|
|
|
95
93
|
| { type: "SET_BROKER_ACCOUNTS"; instanceId: string; accounts: BrokerAccount[] }
|
|
96
94
|
| { type: "SET_INITIALIZED" }
|
|
97
95
|
| { type: "TOGGLE_STATUS_BAR" }
|
|
98
|
-
| { type: "SHOW_GRIDLOCK_TIP" }
|
|
99
|
-
| { type: "DISMISS_GRIDLOCK_TIP" }
|
|
100
96
|
| { type: "SET_THEME"; theme: string }
|
|
101
97
|
| { type: "PREVIEW_THEME"; theme: string | null }
|
|
102
98
|
| { type: "SET_UPDATE_AVAILABLE"; release: ReleaseInfo | null }
|
|
@@ -114,6 +110,12 @@ export type AppAction =
|
|
|
114
110
|
| { type: "SWITCH_LAYOUT"; index: number }
|
|
115
111
|
| { type: "REORDER_LAYOUT"; fromIndex: number; toIndex: number }
|
|
116
112
|
| { type: "NEW_LAYOUT"; name: string }
|
|
113
|
+
| {
|
|
114
|
+
type: "INSTALL_LAYOUT_COPY";
|
|
115
|
+
name: string;
|
|
116
|
+
layout: LayoutConfig;
|
|
117
|
+
paneState: Record<string, PaneRuntimeState>;
|
|
118
|
+
}
|
|
117
119
|
| { type: "DELETE_LAYOUT"; index: number }
|
|
118
120
|
| { type: "RENAME_LAYOUT"; index: number; name: string }
|
|
119
121
|
| { type: "DUPLICATE_LAYOUT"; index: number }
|
|
@@ -372,7 +372,7 @@ export function migrateChartPaneSettings(
|
|
|
372
372
|
settings: Record<string, unknown> | undefined,
|
|
373
373
|
context: LegacyChartMigrationContext = {},
|
|
374
374
|
): Record<string, unknown> | undefined {
|
|
375
|
-
if (context.migrateLegacy
|
|
375
|
+
if (!context.migrateLegacy) return settings;
|
|
376
376
|
if (paneId === "chart-composer") {
|
|
377
377
|
const retained = stripKeys(settings, [
|
|
378
378
|
"chartAxisMode",
|
|
@@ -220,6 +220,7 @@ export function sanitizeLayout(
|
|
|
220
220
|
const layout = cloneLayout(fallback);
|
|
221
221
|
return normalizePaneLayout(layout, {
|
|
222
222
|
defaultFollowSourceInstanceId: getDefaultFollowSourceInstanceId(layout.instances),
|
|
223
|
+
resolveOrphanSymbol: () => null,
|
|
223
224
|
});
|
|
224
225
|
}
|
|
225
226
|
|
|
@@ -242,5 +243,6 @@ export function sanitizeLayout(
|
|
|
242
243
|
|
|
243
244
|
return normalizePaneLayout(layout, {
|
|
244
245
|
defaultFollowSourceInstanceId: getDefaultFollowSourceInstanceId(layout.instances),
|
|
246
|
+
resolveOrphanSymbol: () => null,
|
|
245
247
|
});
|
|
246
248
|
}
|
|
@@ -118,7 +118,7 @@ describe("sanitizeLayout", () => {
|
|
|
118
118
|
expect(getDockedPaneIds(layout)).toEqual(["portfolio-list:main", "ticker-detail:main"]);
|
|
119
119
|
});
|
|
120
120
|
|
|
121
|
-
test("
|
|
121
|
+
test("keeps a ticker research pane safely unlinked when its source is missing", () => {
|
|
122
122
|
const layout = sanitizeLayout({
|
|
123
123
|
dockRoot: { kind: "pane", instanceId: "ticker-detail:main" },
|
|
124
124
|
instances: [
|
|
@@ -132,9 +132,8 @@ describe("sanitizeLayout", () => {
|
|
|
132
132
|
detached: [],
|
|
133
133
|
}, DEFAULT_LAYOUT);
|
|
134
134
|
|
|
135
|
-
expect(findPaneInstance(layout, "ticker-detail:main")).
|
|
136
|
-
expect(getDockedPaneIds(layout)).
|
|
137
|
-
expect(layout.dockRoot).toBeNull();
|
|
135
|
+
expect(findPaneInstance(layout, "ticker-detail:main")?.binding).toEqual({ kind: "none" });
|
|
136
|
+
expect(getDockedPaneIds(layout)).toEqual(["ticker-detail:main"]);
|
|
138
137
|
});
|
|
139
138
|
|
|
140
139
|
test("clamps floating placement memory and drops invalid docked placement hints", () => {
|
|
@@ -211,7 +210,7 @@ describe("sanitizeLayout", () => {
|
|
|
211
210
|
}],
|
|
212
211
|
floating: [],
|
|
213
212
|
detached: [],
|
|
214
|
-
}, DEFAULT_LAYOUT);
|
|
213
|
+
}, DEFAULT_LAYOUT, { migrateLegacy: true });
|
|
215
214
|
|
|
216
215
|
const pane = findPaneInstance(layout, "comparison-chart:main");
|
|
217
216
|
expect(pane?.paneId).toBe("chart-composer");
|
|
@@ -252,7 +251,7 @@ describe("sanitizeLayout", () => {
|
|
|
252
251
|
}],
|
|
253
252
|
floating: [],
|
|
254
253
|
detached: [],
|
|
255
|
-
}, DEFAULT_LAYOUT);
|
|
254
|
+
}, DEFAULT_LAYOUT, { migrateLegacy: true });
|
|
256
255
|
|
|
257
256
|
const settings = findPaneInstance(layout, "ticker-detail:aapl")?.settings;
|
|
258
257
|
expect(settings).toEqual({
|
|
@@ -277,6 +276,28 @@ describe("sanitizeLayout", () => {
|
|
|
277
276
|
expect(settings).not.toHaveProperty("chartResolution");
|
|
278
277
|
});
|
|
279
278
|
|
|
279
|
+
test("does not convert legacy chart settings during ordinary sanitization", () => {
|
|
280
|
+
const layout = sanitizeLayout({
|
|
281
|
+
dockRoot: { kind: "pane", instanceId: "ticker-detail:aapl" },
|
|
282
|
+
instances: [{
|
|
283
|
+
instanceId: "ticker-detail:aapl",
|
|
284
|
+
paneId: "ticker-research",
|
|
285
|
+
binding: { kind: "fixed", symbol: "AAPL" },
|
|
286
|
+
settings: {
|
|
287
|
+
chartRangePreset: "1Y",
|
|
288
|
+
chartResolution: "1wk",
|
|
289
|
+
},
|
|
290
|
+
}],
|
|
291
|
+
floating: [],
|
|
292
|
+
detached: [],
|
|
293
|
+
}, DEFAULT_LAYOUT);
|
|
294
|
+
|
|
295
|
+
expect(findPaneInstance(layout, "ticker-detail:aapl")?.settings).toEqual({
|
|
296
|
+
chartRangePreset: "1Y",
|
|
297
|
+
chartResolution: "1wk",
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
280
301
|
test("falls back to the default layout when given an obsolete column layout", () => {
|
|
281
302
|
const layout = sanitizeLayout({
|
|
282
303
|
columns: [{ width: "100%" }],
|
|
@@ -17,13 +17,11 @@ import { clampFontSize } from "../../../theme/font-scale";
|
|
|
17
17
|
import { isLayoutConfig, sanitizeLayout } from "../layout";
|
|
18
18
|
import { migrateSavedConfig } from "./migrations";
|
|
19
19
|
|
|
20
|
-
const CURRENT_CHART_SANITIZATION = { migrateLegacy: false } as const;
|
|
21
|
-
|
|
22
20
|
export function normalizeLoadedConfig(saved: Record<string, unknown>, dataDir: string): { config: AppConfig; needsSave: boolean } {
|
|
23
21
|
const defaults = createDefaultConfig(dataDir);
|
|
24
22
|
const migration = migrateSavedConfig(saved, dataDir);
|
|
25
23
|
const candidate = migration.config;
|
|
26
|
-
const directLayout = sanitizeLayout(candidate.layout, defaults.layout
|
|
24
|
+
const directLayout = sanitizeLayout(candidate.layout, defaults.layout);
|
|
27
25
|
const layouts = sanitizeSavedLayouts(candidate.layouts, directLayout);
|
|
28
26
|
const activeLayoutIndex = sanitizeActiveLayoutIndex(candidate.activeLayoutIndex, layouts.length);
|
|
29
27
|
const layout = cloneLayout(layouts[activeLayoutIndex]?.layout ?? directLayout);
|
|
@@ -88,7 +86,7 @@ export function normalizeLoadedConfig(saved: Record<string, unknown>, dataDir: s
|
|
|
88
86
|
|
|
89
87
|
export function normalizeConfigForSave(config: AppConfig): AppConfig {
|
|
90
88
|
const defaults = createDefaultConfig(config.dataDir);
|
|
91
|
-
const directLayout = sanitizeLayout(config.layout, defaults.layout
|
|
89
|
+
const directLayout = sanitizeLayout(config.layout, defaults.layout);
|
|
92
90
|
const sanitizedLayouts = sanitizeSavedLayouts(config.layouts, directLayout);
|
|
93
91
|
const activeLayoutIndex = sanitizeActiveLayoutIndex(config.activeLayoutIndex, sanitizedLayouts.length || 1);
|
|
94
92
|
const layout = cloneLayout(sanitizedLayouts[activeLayoutIndex]?.layout ?? directLayout);
|
|
@@ -306,7 +304,7 @@ function sanitizeSavedLayouts(
|
|
|
306
304
|
&& typeof (entry as SavedLayout).name === "string",
|
|
307
305
|
)
|
|
308
306
|
.map((entry) => {
|
|
309
|
-
const layout = sanitizeLayout(entry.layout, fallbackLayout
|
|
307
|
+
const layout = sanitizeLayout(entry.layout, fallbackLayout);
|
|
310
308
|
const paneState = sanitizeSavedPaneState((entry as { paneState?: unknown }).paneState, layout);
|
|
311
309
|
return {
|
|
312
310
|
id: typeof entry.id === "string" ? entry.id : undefined,
|
|
@@ -126,6 +126,17 @@ export class PluginStateStore {
|
|
|
126
126
|
));
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
pluginIds(): string[] {
|
|
130
|
+
return withSqliteBusyRetry("list plugin state ids", () => (
|
|
131
|
+
this.db
|
|
132
|
+
.query<{ plugin_id: string }, []>(
|
|
133
|
+
"SELECT DISTINCT plugin_id FROM plugin_state ORDER BY plugin_id",
|
|
134
|
+
)
|
|
135
|
+
.all()
|
|
136
|
+
.map((row) => row.plugin_id)
|
|
137
|
+
));
|
|
138
|
+
}
|
|
139
|
+
|
|
129
140
|
clear(pluginId: string): void {
|
|
130
141
|
withSqliteBusyRetry("clear plugin state", () => {
|
|
131
142
|
this.db.query("DELETE FROM plugin_state WHERE plugin_id = ?").run(pluginId);
|
|
@@ -221,6 +221,16 @@ describe("AppPersistence", () => {
|
|
|
221
221
|
persistence.close();
|
|
222
222
|
});
|
|
223
223
|
|
|
224
|
+
test("lists plugin ids that have stored state", () => {
|
|
225
|
+
const dbPath = createTempDbPath("plugin-state-ids");
|
|
226
|
+
const persistence = new AppPersistence(dbPath);
|
|
227
|
+
persistence.pluginState.set("gloomberb-cloud", "session", { sessionToken: "token" }, 1);
|
|
228
|
+
persistence.pluginState.set("ai", "provider", "openai", 1);
|
|
229
|
+
|
|
230
|
+
expect(persistence.pluginState.pluginIds()).toEqual(["ai", "gloomberb-cloud"]);
|
|
231
|
+
persistence.close();
|
|
232
|
+
});
|
|
233
|
+
|
|
224
234
|
test("stores multiple plugin state records in one batch", () => {
|
|
225
235
|
const dbPath = createTempDbPath("plugin-state-batch");
|
|
226
236
|
const persistence = new AppPersistence(dbPath);
|
package/src/i18n/es.ts
CHANGED
|
@@ -13,6 +13,7 @@ export const es: Record<string, string> = {
|
|
|
13
13
|
"Cancel": "Cancelar",
|
|
14
14
|
"Close": "Cerrar",
|
|
15
15
|
"Open": "Abrir",
|
|
16
|
+
"Linked to {source}": "Vinculado a {source}",
|
|
16
17
|
"Working…": "Procesando…",
|
|
17
18
|
"Searching…": "Buscando…",
|
|
18
19
|
"Loading...": "Cargando...",
|
|
@@ -220,12 +221,12 @@ export const es: Record<string, string> = {
|
|
|
220
221
|
"Dock Pane": "Anclar panel",
|
|
221
222
|
"Settings": "Ajustes",
|
|
222
223
|
"Copy Screenshot": "Copiar captura",
|
|
224
|
+
"Share Pane": "Compartir panel",
|
|
223
225
|
"Pop Out": "Abrir en ventana",
|
|
224
226
|
"Move Window...": "Mover ventana...",
|
|
225
227
|
"Resize Window...": "Redimensionar ventana...",
|
|
226
228
|
"Move Window": "Mover ventana",
|
|
227
229
|
"Resize Window": "Redimensionar ventana",
|
|
228
|
-
"Gridlock All Windows": "Cuadricular todas las ventanas",
|
|
229
230
|
"Change the current saved layout name": "Cambiar el nombre del diseño guardado actual",
|
|
230
231
|
"Create a copy of the current layout": "Crear una copia del diseño actual",
|
|
231
232
|
"Create a fresh saved layout": "Crear un nuevo diseño guardado",
|
|
@@ -517,7 +518,6 @@ export const es: Record<string, string> = {
|
|
|
517
518
|
"Pop the focused pane out to a desktop window.": "Extraer el panel activo a una ventana de escritorio.",
|
|
518
519
|
"Copy a screenshot of the focused pane.": "Copiar una captura del panel activo.",
|
|
519
520
|
"Open layout actions.": "Abrir acciones de diseño.",
|
|
520
|
-
"Gridlock all windows.": "Cuadricular todas las ventanas.",
|
|
521
521
|
"Enter window move mode.": "Entrar en modo mover ventana.",
|
|
522
522
|
"Enter window resize mode.": "Entrar en modo redimensionar ventana.",
|
|
523
523
|
"Cancel an active pane drag.": "Cancelar el arrastre de panel activo.",
|
|
@@ -897,8 +897,7 @@ export const es: Record<string, string> = {
|
|
|
897
897
|
"v{version} available — run {command}": "v{version} disponible — ejecuta {command}",
|
|
898
898
|
"v{version} available — starting download...": "v{version} disponible — iniciando descarga...",
|
|
899
899
|
"command bar": "barra de comandos",
|
|
900
|
-
"
|
|
901
|
-
"Gridlock All": "Cuadricular todo",
|
|
900
|
+
"Tidy Windows": "Ordenar ventanas",
|
|
902
901
|
"Dismiss": "Descartar",
|
|
903
902
|
"Minimize": "Minimizar",
|
|
904
903
|
"Maximize": "Maximizar",
|
|
@@ -1061,6 +1060,16 @@ export const es: Record<string, string> = {
|
|
|
1061
1060
|
"Signed in as {email}": "Sesión iniciada como {email}",
|
|
1062
1061
|
"Signed in to Gloom Cloud": "Sesión iniciada en Gloom Cloud",
|
|
1063
1062
|
"Signing you in...": "Iniciando sesión...",
|
|
1063
|
+
"Forgot password?": "¿Olvidaste tu contraseña?",
|
|
1064
|
+
"Sending reset link...": "Enviando enlace de restablecimiento...",
|
|
1065
|
+
"Reset link sent to {email}. Check your inbox.": "Enlace de restablecimiento enviado a {email}. Revisa tu bandeja de entrada.",
|
|
1066
|
+
"Could not send the reset email.": "No se pudo enviar el correo de restablecimiento.",
|
|
1067
|
+
"Create Account": "Crear cuenta",
|
|
1068
|
+
"Log in instead": "Inicia sesión en su lugar",
|
|
1069
|
+
"Sign up instead": "Regístrate en su lugar",
|
|
1070
|
+
"We'll email you a verification link.": "Te enviaremos un enlace de verificación por correo.",
|
|
1071
|
+
"show": "mostrar",
|
|
1072
|
+
"hide": "ocultar",
|
|
1064
1073
|
"signing in...": "iniciando sesión...",
|
|
1065
1074
|
"Could not sign you in.": "No se pudo iniciar tu sesión.",
|
|
1066
1075
|
"Signs this terminal in to your account.": "Inicia sesión en tu cuenta desde esta terminal.",
|
|
@@ -1107,7 +1116,6 @@ export const es: Record<string, string> = {
|
|
|
1107
1116
|
"Revisa tu bandeja de entrada para verificar tu correo. Después puedes empezar tu prueba Pro gratis de 7 días cuando quieras: escribe UPGRADE.",
|
|
1108
1117
|
|
|
1109
1118
|
// ── Onboarding setup ────────────────────────────────────────
|
|
1110
|
-
"$29/mo for real-time market data": "$29/mes por datos de mercado en tiempo real",
|
|
1111
1119
|
"Limited founding offer. Start a free 7-day trial.": "Oferta limitada de lanzamiento. Empieza una prueba gratuita de 7 días.",
|
|
1112
1120
|
"Start 7-day free trial": "Iniciar prueba gratuita de 7 días",
|
|
1113
1121
|
"Adds quotes, financials, options, research, news, chat and AI commands.": "Añade cotizaciones, datos financieros, opciones, análisis, noticias, chat y comandos de IA.",
|