gloomberb 0.11.3 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -4
- package/README.zh-CN.md +4 -2
- package/package.json +28 -2
- package/src/api-client/auth.ts +12 -1
- package/src/api-client/data.ts +103 -1
- package/src/api-client/index.test.ts +127 -0
- package/src/api-client/index.ts +139 -3
- package/src/api-client/normalizers.ts +39 -0
- package/src/api-client/paths.ts +85 -1
- package/src/api-client/research-activity.ts +61 -0
- package/src/api-client/types.ts +197 -0
- package/src/app/pane-runtime/index.tsx +15 -12
- package/src/app/pane-runtime/layout-placement.test.ts +27 -0
- package/src/app/pane-runtime/layout-placement.ts +13 -0
- package/src/app/pane-runtime/plugin-bindings.ts +62 -0
- package/src/app.tsx +60 -18
- package/src/architecture/browser-entry-graph.test.ts +96 -0
- package/src/brokers/profile-form.test.ts +25 -27
- package/src/brokers/test-broker.ts +68 -0
- package/src/cli/commands/plugins.ts +43 -14
- package/src/cli/desktop-pane-shot.ts +362 -24
- package/src/cli/index.ts +1 -1
- package/src/cli/pane-functions/capabilities.ts +126 -43
- package/src/cli/pane-functions/catalog.ts +16 -4
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/dom.test.ts +41 -0
- package/src/cli/pane-functions/dom.ts +199 -0
- package/src/cli/pane-functions/headless.test.ts +247 -0
- package/src/cli/pane-functions/headless.ts +423 -0
- package/src/cli/pane-functions/index.test.ts +20 -0
- package/src/cli/pane-functions/index.ts +10 -6
- package/src/cli/pane-functions/intraday-shot.test.ts +307 -0
- package/src/cli/pane-functions/intraday-shot.ts +334 -0
- package/src/cli/pane-functions/options.ts +27 -2
- package/src/cli/pane-functions/report.test.ts +29 -1
- package/src/cli/pane-functions/report.ts +69 -4
- package/src/cli/pane-functions/resolver.ts +26 -8
- package/src/cli/pane-functions/screenshot.test.ts +76 -0
- package/src/cli/pane-functions/screenshot.ts +428 -42
- package/src/cli/restore-plugins.ts +41 -0
- package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
- package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
- package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
- package/src/components/chart/composite/composite-chart.test.tsx +136 -21
- package/src/components/chart/composite/composite-chart.tsx +378 -215
- package/src/components/chart/composite/format.ts +8 -2
- package/src/components/chart/composite/interactions.test.ts +68 -102
- package/src/components/chart/composite/interactions.ts +214 -264
- package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
- package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
- package/src/components/chart/composite/rasterizer.ts +33 -0
- package/src/components/chart/composite/renderers.test.ts +51 -4
- package/src/components/chart/composite/scene.test.ts +128 -2
- package/src/components/chart/composite/scene.ts +149 -19
- package/src/components/chart/composite/text-renderer.ts +16 -2
- package/src/components/chart/composite/time-scale.ts +163 -62
- package/src/components/chart/composite/types.ts +51 -1
- package/src/components/chart/core/data.ts +9 -13
- package/src/components/chart/core/date-window.ts +7 -1
- package/src/components/chart/core/pointer.ts +3 -0
- package/src/components/chart/core/types.ts +0 -45
- package/src/components/chart/native/chart-rasterizer.ts +0 -1
- package/src/components/chart/native/kitty/index.test.ts +0 -188
- package/src/components/chart/native/raster/crosshair.ts +6 -5
- package/src/components/chart/native/raster/primitives.ts +0 -23
- package/src/components/chart/native/renderer-selection.ts +81 -31
- package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
- package/src/components/chart/static/chart-surface.tsx +150 -0
- package/src/components/chart/static/index.ts +8 -0
- package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
- package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
- package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
- package/src/components/chart/static/series.ts +49 -0
- package/src/components/command-bar/action-runtime.ts +1 -3
- package/src/components/command-bar/assist/model.test.ts +17 -2
- package/src/components/command-bar/assist/model.ts +40 -9
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +1 -1
- package/src/components/command-bar/commands/registry.ts +0 -11
- package/src/components/command-bar/helpers.test.ts +2 -2
- package/src/components/command-bar/helpers.ts +2 -6
- package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
- package/src/components/command-bar/list/badge.test.ts +54 -0
- package/src/components/command-bar/list/badge.ts +62 -0
- package/src/components/command-bar/list/model.test.ts +103 -0
- package/src/components/command-bar/list/model.ts +61 -4
- package/src/components/command-bar/list/view.test.tsx +113 -0
- package/src/components/command-bar/list/view.tsx +147 -131
- package/src/components/command-bar/pane-templates/workflow.ts +1 -1
- package/src/components/command-bar/panel/body.tsx +51 -65
- package/src/components/command-bar/panel/index.tsx +48 -13
- package/src/components/command-bar/panel/layout.test.ts +139 -0
- package/src/components/command-bar/panel/layout.ts +65 -28
- package/src/components/command-bar/panel/native-surface.ts +24 -0
- package/src/components/command-bar/panel/prompt-binding.ts +54 -0
- package/src/components/command-bar/panel/runtime.ts +6 -3
- package/src/components/command-bar/panel/state.ts +41 -7
- package/src/components/command-bar/panel/types.ts +7 -1
- package/src/components/command-bar/routes/root/provider-search.ts +34 -15
- package/src/components/command-bar/routes/root/results.test.ts +161 -0
- package/src/components/command-bar/routes/root/results.ts +30 -28
- package/src/components/command-bar/routes/root/runtime.ts +37 -20
- package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
- package/src/components/command-bar/routes/root/search-providers.ts +205 -0
- package/src/components/command-bar/routes/root/selection.ts +1 -12
- package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
- package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
- package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
- package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
- package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
- package/src/components/command-bar/routing/actions.ts +1 -81
- package/src/components/command-bar/routing/launch-request.ts +1 -1
- package/src/components/command-bar/routing/list-state.ts +6 -21
- package/src/components/command-bar/selection-runtime.ts +1 -1
- package/src/components/command-bar/surface/assist.test.tsx +34 -19
- package/src/components/command-bar/surface/environment.ts +8 -1
- package/src/components/command-bar/surface/index.test.tsx +84 -68
- package/src/components/command-bar/surface/index.tsx +33 -36
- package/src/components/command-bar/surface/test-harness.tsx +38 -7
- package/src/components/command-bar/theme-picker.tsx +43 -15
- package/src/components/command-bar/view-model.test.ts +53 -8
- package/src/components/command-bar/view-model.ts +43 -14
- package/src/components/command-bar/workflow/tickers.ts +1 -0
- package/src/components/command-bar/workflow/types.ts +1 -1
- package/src/components/data-table/view.test.tsx +45 -6
- package/src/components/data-table/view.tsx +55 -9
- package/src/components/index.ts +10 -2
- package/src/components/input-search-bar.test.tsx +36 -2
- package/src/components/input-search-bar.tsx +18 -1
- package/src/components/layout/header.test.tsx +57 -10
- package/src/components/layout/header.tsx +292 -149
- package/src/components/layout/market-summary.test.ts +81 -0
- package/src/components/layout/market-summary.ts +109 -0
- package/src/components/layout/shell/chrome.ts +96 -1
- package/src/components/layout/shell/index.tsx +11 -3
- package/src/components/layout/shell/menu.ts +9 -0
- package/src/components/layout/shell/pane/actions.ts +29 -0
- package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
- package/src/components/layout/shell/shortcuts.ts +3 -0
- package/src/components/layout/status-bar.test.tsx +9 -8
- package/src/components/layout/status-bar.tsx +85 -54
- package/src/components/onboarding/account-step/chooser-panel.tsx +2 -2
- package/src/components/onboarding/account-step/qr-panel.tsx +2 -10
- package/src/components/onboarding/onboarding-frame.tsx +2 -26
- package/src/components/onboarding/onboarding-wizard.test.tsx +38 -12
- package/src/components/onboarding/onboarding-wizard.tsx +49 -14
- package/src/components/price-sparkline/index.ts +61 -34
- package/src/components/price-sparkline/view.tsx +1 -1
- package/src/components/sign-in-gate.test.tsx +128 -0
- package/src/components/sign-in-gate.tsx +149 -0
- package/src/components/speedometer-gauge/desktop.tsx +2 -12
- package/src/components/ticker/badge/list.tsx +17 -3
- package/src/components/ticker/list-table-view.tsx +6 -3
- package/src/components/ui/data-table/index.tsx +3 -2
- package/src/components/ui/data-table/opentui/index.tsx +165 -120
- package/src/components/ui/frame.tsx +28 -0
- package/src/components/ui/index.ts +3 -0
- package/src/components/ui/native-select.tsx +20 -10
- package/src/components/ui/select-button.tsx +138 -0
- package/src/components/ui/status.tsx +2 -2
- package/src/components/ui/table-layout.ts +1 -1
- package/src/components/use-double-click-activation.ts +7 -3
- package/src/core/app-services.ts +3 -3
- package/src/data/config/layout.ts +12 -6
- package/src/data/config/store/index.test.ts +120 -0
- package/src/data/config/store/migrations.ts +41 -0
- package/src/data/config/store/node.ts +7 -1
- package/src/data/config/store/normalize.ts +9 -3
- package/src/i18n/es.ts +6 -2
- package/src/i18n/ja.ts +6 -2
- package/src/i18n/ko.ts +6 -2
- package/src/i18n/zh-cn.ts +6 -2
- package/src/i18n/zh-tw.ts +6 -2
- package/src/layout-marketplace/pane-imagery.ts +1 -0
- package/src/layout-marketplace/payload.test.ts +17 -0
- package/src/layout-marketplace/payload.ts +40 -5
- package/src/market-data/coordinator/index.ts +3 -2
- package/src/market-data/coordinator/quotes.ts +173 -66
- package/src/market-data/coordinator/subscriptions.test.ts +215 -37
- package/src/pane-settings.ts +1 -1
- package/src/plugins/builtin/analytics/pane-model.ts +1 -1
- package/src/plugins/builtin/analytics/view.tsx +1 -1
- package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
- package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
- package/src/plugins/builtin/cds/client.test.ts +20 -0
- package/src/plugins/builtin/cds/client.ts +13 -3
- package/src/plugins/builtin/cds/headless.test.ts +30 -0
- package/src/plugins/builtin/cds/headless.ts +113 -0
- package/src/plugins/builtin/cds/index.tsx +4 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -1
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
- package/src/plugins/builtin/chat/content/composer.tsx +8 -4
- package/src/plugins/builtin/cloud/askg/protocol.test.ts +189 -0
- package/src/plugins/builtin/cloud/askg/protocol.ts +274 -0
- package/src/plugins/builtin/cloud/auth-dialog.tsx +19 -227
- package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
- package/src/plugins/builtin/cloud/device-signin-dialog.tsx +21 -8
- package/src/plugins/builtin/cloud/plugin.tsx +1 -0
- package/src/plugins/builtin/cloud/verification-panel.tsx +56 -0
- package/src/plugins/builtin/composite-plugins.ts +14 -1
- package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
- package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
- package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
- package/src/plugins/builtin/credit-conditions/client.ts +1 -1
- package/src/plugins/builtin/credit-conditions/headless.test.ts +31 -0
- package/src/plugins/builtin/credit-conditions/headless.ts +55 -0
- package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
- package/src/plugins/builtin/credit-conditions/index.tsx +6 -21
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/earnings-calls/data.ts +223 -0
- package/src/plugins/builtin/earnings-calls/format.ts +49 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +760 -0
- package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
- package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +337 -0
- package/src/plugins/builtin/econ/detail-view.tsx +1 -2
- package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
- package/src/plugins/builtin/econ/index.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
- package/src/plugins/builtin/econ-statistics/client.ts +92 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
- package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
- package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
- package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
- package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
- package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
- package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
- package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
- package/src/plugins/builtin/econ-statistics/view.ts +90 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
- package/src/plugins/builtin/fear-greed/data.ts +6 -1
- package/src/plugins/builtin/fear-greed/headless.test.ts +53 -0
- package/src/plugins/builtin/fear-greed/headless.ts +84 -0
- package/src/plugins/builtin/fear-greed/index.tsx +4 -0
- package/src/plugins/builtin/help/index.tsx +4 -0
- package/src/plugins/builtin/holders/index.tsx +1 -0
- package/src/plugins/builtin/insider/index.tsx +1 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
- package/src/plugins/builtin/market-movers/client.ts +76 -0
- package/src/plugins/builtin/market-movers/headless.test.ts +34 -0
- package/src/plugins/builtin/market-movers/headless.ts +73 -0
- package/src/plugins/builtin/market-movers/index.tsx +11 -51
- package/src/plugins/builtin/market-valuation/align.ts +122 -0
- package/src/plugins/builtin/market-valuation/cache.ts +32 -0
- package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
- package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
- package/src/plugins/builtin/market-valuation/client.ts +116 -0
- package/src/plugins/builtin/market-valuation/defs.ts +251 -0
- package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
- package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
- package/src/plugins/builtin/market-valuation/headless.ts +170 -0
- package/src/plugins/builtin/market-valuation/index.tsx +64 -0
- package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
- package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
- package/src/plugins/builtin/market-valuation/model.ts +8 -0
- package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
- package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
- package/src/plugins/builtin/market-valuation/series.ts +12 -0
- package/src/plugins/builtin/market-valuation/settings.ts +38 -0
- package/src/plugins/builtin/market-valuation/sources.ts +130 -0
- package/src/plugins/builtin/market-valuation/trend.ts +89 -0
- package/src/plugins/builtin/market-valuation/view.ts +137 -0
- package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
- package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
- package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
- package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
- package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
- package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
- package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
- package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
- package/src/plugins/builtin/news/wire/index.tsx +31 -44
- package/src/plugins/builtin/options/index.tsx +1 -0
- package/src/plugins/builtin/options/view.test.tsx +29 -1
- package/src/plugins/builtin/options/view.tsx +1 -22
- package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
- package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
- package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
- package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
- package/src/plugins/builtin/plugin-module.ts +9 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -237
- package/src/plugins/builtin/research/event-model.ts +245 -0
- package/src/plugins/builtin/research/events-headless.test.ts +32 -0
- package/src/plugins/builtin/research/events-headless.ts +87 -0
- package/src/plugins/builtin/research/index.tsx +8 -0
- package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
- package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
- package/src/plugins/builtin/research-search/data.ts +109 -0
- package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
- package/src/plugins/builtin/research-search/document-view.tsx +254 -0
- package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
- package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
- package/src/plugins/builtin/research-search/index.tsx +85 -0
- package/src/plugins/builtin/research-search/model.test.ts +40 -0
- package/src/plugins/builtin/research-search/model.ts +291 -0
- package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
- package/src/plugins/builtin/research-search/pane.tsx +682 -0
- package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
- package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
- package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
- package/src/plugins/builtin/research-search/snippet.ts +134 -0
- package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
- package/src/plugins/builtin/scanner/flow-model.ts +29 -1
- package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
- package/src/plugins/builtin/scanner/headless.test.ts +59 -0
- package/src/plugins/builtin/scanner/headless.ts +125 -0
- package/src/plugins/builtin/scanner/index.ts +4 -0
- package/src/plugins/builtin/sec/index.tsx +1 -0
- package/src/plugins/builtin/sectors/client.ts +54 -0
- package/src/plugins/builtin/sectors/headless.test.ts +28 -0
- package/src/plugins/builtin/sectors/headless.ts +80 -0
- package/src/plugins/builtin/sectors/index.tsx +6 -51
- package/src/plugins/builtin/shared/cloud-upgrade.ts +8 -4
- package/src/plugins/builtin/shared/series-cache.ts +93 -0
- package/src/plugins/builtin/short-interest/index.tsx +1 -0
- package/src/plugins/builtin/short-interest/pane.tsx +1 -1
- package/src/plugins/builtin/thirteenf/index.tsx +1 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +0 -14
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -0
- package/src/plugins/builtin/treasury-auctions/headless.test.ts +34 -0
- package/src/plugins/builtin/treasury-auctions/headless.ts +95 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +4 -0
- package/src/plugins/builtin/volatility/client.ts +16 -3
- package/src/plugins/builtin/volatility/headless.test.ts +35 -0
- package/src/plugins/builtin/volatility/headless.ts +75 -0
- package/src/plugins/builtin/volatility/index.tsx +5 -1
- package/src/plugins/builtin/world-indices/client.ts +43 -0
- package/src/plugins/builtin/world-indices/headless.test.ts +32 -0
- package/src/plugins/builtin/world-indices/headless.ts +109 -0
- package/src/plugins/builtin/world-indices/index.tsx +4 -0
- package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +213 -69
- package/src/plugins/builtin/world-venue-map/model.test.ts +32 -0
- package/src/plugins/builtin/world-venue-map/model.ts +134 -13
- package/src/plugins/builtin/yield-curve/headless.test.ts +24 -0
- package/src/plugins/builtin/yield-curve/headless.ts +67 -0
- package/src/plugins/builtin/yield-curve/index.tsx +7 -9
- package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -6
- package/src/plugins/bundle.test.ts +166 -0
- package/src/plugins/bundle.ts +141 -0
- package/src/plugins/catalog-backend.test.ts +26 -0
- package/src/plugins/catalog-backend.ts +9 -2
- package/src/plugins/catalog-browser.test.ts +1 -13
- package/src/plugins/catalog-browser.ts +14 -0
- package/src/plugins/catalog-manifest.test.ts +58 -0
- package/src/plugins/catalog-manifest.ts +99 -0
- package/src/plugins/catalog-ui.ts +20 -10
- package/src/plugins/catalog.ts +9 -2
- package/src/plugins/current-target.ts +28 -0
- package/src/plugins/host-contract.ts +46 -0
- package/src/plugins/host-link.ts +158 -0
- package/src/plugins/host-modules.ts +73 -0
- package/src/plugins/loader.ts +90 -23
- package/src/plugins/ownership.ts +1 -0
- package/src/plugins/prediction-markets/index.tsx +1 -0
- package/src/plugins/registry/context.ts +3 -0
- package/src/plugins/registry/contributions.ts +27 -0
- package/src/plugins/registry/index.test.ts +25 -0
- package/src/plugins/registry/index.ts +8 -0
- package/src/plugins/registry/pane-settings.ts +6 -13
- package/src/plugins/seed.test.ts +83 -0
- package/src/plugins/seed.ts +83 -0
- package/src/public/broker.ts +37 -0
- package/src/public/market-data.ts +9 -0
- package/src/public/pane-hooks.ts +57 -0
- package/src/public/public-api.test.ts +285 -0
- package/src/public/react.ts +62 -0
- package/src/public/test-support.ts +23 -0
- package/src/public/time-series.ts +8 -0
- package/src/public/utils.ts +77 -0
- package/src/remote/layout-helpers.ts +6 -8
- package/src/remote/resources.ts +2 -0
- package/src/remote/schema.test.ts +65 -0
- package/src/remote/schema.ts +323 -39
- package/src/remote/semantic-tree.test.tsx +105 -0
- package/src/remote/semantic-tree.tsx +53 -35
- package/src/remote/types.ts +49 -2
- package/src/renderers/browser/app-services.ts +3 -0
- package/src/renderers/browser/config-host.ts +24 -8
- package/src/renderers/browser/deeplink-bridge.test.ts +11 -0
- package/src/renderers/browser/deeplink-bridge.ts +15 -3
- package/src/renderers/browser/main.tsx +8 -0
- package/src/renderers/browser/research-entry.test.ts +8 -0
- package/src/renderers/browser/research-entry.ts +7 -0
- package/src/renderers/cloudflare/worker.test.ts +19 -0
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +2 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +10 -3
- package/src/renderers/electrobun/bun/external-plugins.ts +156 -0
- package/src/renderers/electrobun/bun/index.ts +5 -2
- package/src/renderers/electrobun/shared/protocol.ts +21 -0
- package/src/renderers/electrobun/view/build-assets.ts +0 -3
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +114 -11
- package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
- package/src/renderers/electrobun/view/external-plugins.ts +63 -0
- package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
- package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
- package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
- package/src/renderers/electrobun/view/host/mouse.ts +16 -2
- package/src/renderers/electrobun/view/main.tsx +27 -2
- package/src/renderers/electrobun/view/styles.css +5 -0
- package/src/renderers/electrobun/view/toast-host.tsx +18 -0
- package/src/renderers/opentui/chart-surface.tsx +5 -1
- package/src/renderers/opentui/host.tsx +16 -2
- package/src/renderers/opentui/interaction-performance.test.ts +79 -0
- package/src/renderers/opentui/interaction-performance.ts +188 -0
- package/src/renderers/opentui/start.tsx +26 -1
- package/src/renderers/opentui/test-utils.tsx +13 -0
- package/src/renderers/opentui/toast-host.tsx +26 -2
- package/src/renderers/opentui/ui-host.tsx +1 -0
- package/src/sources/provider-router/search.test.ts +191 -0
- package/src/sources/provider-router/search.ts +92 -39
- package/src/state/app/context/index.tsx +2 -0
- package/src/state/app/context/selector.test.tsx +33 -1
- package/src/state/hooks/quote-streaming.test.tsx +93 -0
- package/src/state/hooks/quote-streaming.ts +106 -30
- package/src/state/pane-table-export-registry.ts +19 -0
- package/src/sync/core-contributors.test.ts +111 -0
- package/src/sync/core-contributors.ts +155 -1
- package/src/theme/color-utils.ts +1 -1
- package/src/theme/colors.ts +50 -0
- package/src/theme/themes.test.ts +15 -2
- package/src/theme/themes.ts +19 -1
- package/src/tickers/search/index.test.ts +26 -0
- package/src/tickers/search/index.ts +42 -17
- package/src/tickers/search/ranking.ts +14 -0
- package/src/time-series/chart-data.test.ts +216 -0
- package/src/time-series/resolution.test.ts +25 -26
- package/src/time-series/resolution.ts +41 -10
- package/src/time-series/resolve.test.ts +118 -1
- package/src/time-series/resolve.ts +21 -16
- package/src/time-series/types.ts +3 -1
- package/src/time-series/use-chart-resolution.ts +5 -0
- package/src/types/config.ts +125 -3
- package/src/types/data-provider.ts +11 -0
- package/src/types/headless.ts +161 -0
- package/src/types/plugin.ts +102 -0
- package/src/ui/host.tsx +12 -1
- package/src/ui/index.tsx +1 -0
- package/src/ui/toast.tsx +8 -4
- package/src/utils/format.ts +35 -0
- package/src/utils/fuzzy-search.test.ts +94 -0
- package/src/utils/fuzzy-search.ts +81 -22
- package/src/utils/shortcut-labels.ts +14 -0
- package/src/components/chart/core/axis-format.ts +0 -157
- package/src/components/chart/core/index.test.ts +0 -1075
- package/src/components/chart/core/projection.ts +0 -357
- package/src/components/chart/core/render-utils.ts +0 -4
- package/src/components/chart/core/renderer.ts +0 -244
- package/src/components/chart/core/scene.ts +0 -180
- package/src/components/chart/core/time-axis.ts +0 -330
- package/src/components/chart/cursor-motion.ts +0 -47
- package/src/components/chart/indicators/types.ts +0 -26
- package/src/components/chart/market-session.ts +0 -176
- package/src/components/chart/multi-line/renderer.test.ts +0 -83
- package/src/components/chart/multi-line/renderer.ts +0 -161
- package/src/components/chart/multi-line/scene.ts +0 -188
- package/src/components/chart/native/raster/layout.ts +0 -129
- package/src/components/chart/native/raster/price-chart.ts +0 -244
- package/src/components/chart/rendering/chart-draw.ts +0 -270
- package/src/components/chart/rendering/geometry.ts +0 -95
- package/src/components/chart/rendering/pixel-buffer.ts +0 -267
- package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
- package/src/components/chart/scatter-chart-renderer.ts +0 -223
- package/src/components/chart/static/chart/surface.tsx +0 -358
- package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
- package/src/components/command-bar/routes/root/article-results.ts +0 -55
- package/src/plugins/broker-sync/broker-sync.test.ts +0 -188
- package/src/plugins/broker-sync/native-loader.ts +0 -10
- package/src/plugins/broker-sync/normalize.ts +0 -308
- package/src/plugins/broker-sync/public.ts +0 -122
- package/src/plugins/broker-sync/robinhood-native.ts +0 -367
- package/src/plugins/broker-sync/robinhood.ts +0 -114
- package/src/plugins/broker-sync/simplefin-native.ts +0 -201
- package/src/plugins/broker-sync/simplefin.ts +0 -70
- package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
- package/src/plugins/builtin/news/wire/article-search.ts +0 -140
- package/src/plugins/builtin/substack/api/auth.ts +0 -185
- package/src/plugins/builtin/substack/api/cache.ts +0 -82
- package/src/plugins/builtin/substack/api/loaders.ts +0 -170
- package/src/plugins/builtin/substack/api/store.ts +0 -186
- package/src/plugins/builtin/substack/api/types.ts +0 -42
- package/src/plugins/builtin/substack/api.test.ts +0 -298
- package/src/plugins/builtin/substack/article-detail.tsx +0 -397
- package/src/plugins/builtin/substack/article-stack.tsx +0 -133
- package/src/plugins/builtin/substack/content.ts +0 -478
- package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
- package/src/plugins/builtin/substack/index.tsx +0 -49
- package/src/plugins/builtin/substack/login-view.tsx +0 -126
- package/src/plugins/builtin/substack/model.test.ts +0 -155
- package/src/plugins/builtin/substack/normalize.ts +0 -343
- package/src/plugins/builtin/substack/pane-footer.ts +0 -52
- package/src/plugins/builtin/substack/pane-state.ts +0 -170
- package/src/plugins/builtin/substack/pane.tsx +0 -547
- package/src/plugins/builtin/substack/read-state.ts +0 -30
- package/src/plugins/builtin/substack/table.ts +0 -111
- package/src/plugins/builtin/substack/types.ts +0 -71
- package/src/plugins/builtin/substack/utils.ts +0 -131
- package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
- package/src/plugins/ibkr/account-cache.test.ts +0 -102
- package/src/plugins/ibkr/account-cache.ts +0 -24
- package/src/plugins/ibkr/broker-adapter.ts +0 -286
- package/src/plugins/ibkr/config.test.ts +0 -79
- package/src/plugins/ibkr/config.ts +0 -250
- package/src/plugins/ibkr/dialogs.tsx +0 -40
- package/src/plugins/ibkr/flex/index.test.ts +0 -264
- package/src/plugins/ibkr/flex/index.ts +0 -367
- package/src/plugins/ibkr/flex/statement-client.ts +0 -235
- package/src/plugins/ibkr/fundamental-parser.ts +0 -149
- package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
- package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
- package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
- package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
- package/src/plugins/ibkr/gateway/connection.ts +0 -152
- package/src/plugins/ibkr/gateway/contracts.ts +0 -132
- package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
- package/src/plugins/ibkr/gateway/helpers.ts +0 -33
- package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
- package/src/plugins/ibkr/gateway/history.ts +0 -120
- package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
- package/src/plugins/ibkr/gateway/market-data.ts +0 -345
- package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
- package/src/plugins/ibkr/gateway/orders.ts +0 -63
- package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
- package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
- package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
- package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
- package/src/plugins/ibkr/gateway/selection.ts +0 -51
- package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
- package/src/plugins/ibkr/gateway/service/index.ts +0 -429
- package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
- package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
- package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
- package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
- package/src/plugins/ibkr/gateway/service/native.ts +0 -543
- package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
- package/src/plugins/ibkr/gateway/types.ts +0 -27
- package/src/plugins/ibkr/index.test.tsx +0 -162
- package/src/plugins/ibkr/index.tsx +0 -159
- package/src/plugins/ibkr/instance-selection.test.ts +0 -120
- package/src/plugins/ibkr/instance-selection.ts +0 -36
- package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
- package/src/plugins/ibkr/portfolio-performance.ts +0 -129
- package/src/plugins/ibkr/trade/badge.tsx +0 -28
- package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
- package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
- package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
- package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
- package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
- package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
- package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
- package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
- package/src/plugins/ibkr/trade/tab/model.ts +0 -159
- package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
- package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
- package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
- package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
- package/src/plugins/ibkr/trade/utils.test.ts +0 -175
- package/src/plugins/ibkr/trade/utils.ts +0 -144
- package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
- package/src/plugins/ibkr/trading/pane.tsx +0 -332
- package/src/plugins/ibkr/trading/state.test.ts +0 -109
- package/src/plugins/ibkr/trading/state.ts +0 -335
- package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +0 -7
|
@@ -10,8 +10,10 @@ import {
|
|
|
10
10
|
cloneLayout,
|
|
11
11
|
clonePaneSettings,
|
|
12
12
|
createPaneInstanceId,
|
|
13
|
+
getPlacedPaneInstanceIds,
|
|
13
14
|
normalizePaneLayout,
|
|
14
15
|
normalizePaneId,
|
|
16
|
+
removeUnreachablePaneInstances,
|
|
15
17
|
} from "../../types/config";
|
|
16
18
|
import {
|
|
17
19
|
migrateChartPaneSettings,
|
|
@@ -155,11 +157,14 @@ function sanitizePaneInstances(
|
|
|
155
157
|
placementMemory: sanitizePlacementMemory(entry.placementMemory),
|
|
156
158
|
};
|
|
157
159
|
});
|
|
158
|
-
return instances
|
|
160
|
+
return instances;
|
|
159
161
|
}
|
|
160
162
|
|
|
161
|
-
function getDefaultFollowSourceInstanceId(
|
|
162
|
-
|
|
163
|
+
function getDefaultFollowSourceInstanceId(layout: LayoutConfig): string | null {
|
|
164
|
+
const placedPaneIds = new Set(getPlacedPaneInstanceIds(layout));
|
|
165
|
+
return layout.instances.find((instance) => (
|
|
166
|
+
instance.paneId === "portfolio-list" && placedPaneIds.has(instance.instanceId)
|
|
167
|
+
))?.instanceId ?? null;
|
|
163
168
|
}
|
|
164
169
|
|
|
165
170
|
function sanitizeFloatingEntries(value: unknown, validInstanceIds: Set<string>): LayoutConfig["floating"] {
|
|
@@ -219,7 +224,7 @@ export function sanitizeLayout(
|
|
|
219
224
|
if (!Array.isArray((value as LayoutConfig & { instances?: unknown }).instances)) {
|
|
220
225
|
const layout = cloneLayout(fallback);
|
|
221
226
|
return normalizePaneLayout(layout, {
|
|
222
|
-
defaultFollowSourceInstanceId: getDefaultFollowSourceInstanceId(layout
|
|
227
|
+
defaultFollowSourceInstanceId: getDefaultFollowSourceInstanceId(layout),
|
|
223
228
|
resolveOrphanSymbol: () => null,
|
|
224
229
|
});
|
|
225
230
|
}
|
|
@@ -241,8 +246,9 @@ export function sanitizeLayout(
|
|
|
241
246
|
detached,
|
|
242
247
|
};
|
|
243
248
|
|
|
244
|
-
|
|
245
|
-
defaultFollowSourceInstanceId: getDefaultFollowSourceInstanceId(layout
|
|
249
|
+
const normalized = normalizePaneLayout(layout, {
|
|
250
|
+
defaultFollowSourceInstanceId: getDefaultFollowSourceInstanceId(layout),
|
|
246
251
|
resolveOrphanSymbol: () => null,
|
|
247
252
|
});
|
|
253
|
+
return removeUnreachablePaneInstances(normalized);
|
|
248
254
|
}
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
findPaneInstance,
|
|
12
12
|
} from "../../../types/config";
|
|
13
13
|
import { getDockedPaneIds } from "../../../plugins/pane-manager";
|
|
14
|
+
import { EXTRACTED_PLUGINS, seedExtractedPlugins } from "../../../plugins/seed";
|
|
14
15
|
|
|
15
16
|
const tempDirs: string[] = [];
|
|
16
17
|
|
|
@@ -48,7 +49,73 @@ async function writeConfigJson(dataDir: string, config: Record<string, unknown>)
|
|
|
48
49
|
await writeFile(join(dataDir, "config.json"), JSON.stringify(config), "utf-8");
|
|
49
50
|
}
|
|
50
51
|
|
|
52
|
+
test("fresh installs skip plugin restoration across config reloads", async () => {
|
|
53
|
+
const dataDir = await createTempConfigDir();
|
|
54
|
+
const fresh = await loadConfig(dataDir);
|
|
55
|
+
await saveConfig(fresh);
|
|
56
|
+
const reloaded = await loadConfig(dataDir);
|
|
57
|
+
const installed: string[] = [];
|
|
58
|
+
await seedExtractedPlugins(reloaded, async (ref) => { installed.push(ref); }, dataDir);
|
|
59
|
+
expect(installed).toEqual([]);
|
|
60
|
+
expect(reloaded.seededPlugins).toEqual(EXTRACTED_PLUGINS.map((plugin) => plugin.id));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("existing users retain pending and completed plugin migrations", async () => {
|
|
64
|
+
const dataDir = await createTempConfigDir();
|
|
65
|
+
await writeConfigJson(dataDir, createSavedConfig());
|
|
66
|
+
expect((await loadConfig(dataDir)).seededPlugins).toEqual([]);
|
|
67
|
+
await writeConfigJson(dataDir, createSavedConfig({ seededPlugins: ["substack", "substack", 42] }));
|
|
68
|
+
const loaded = await loadConfig(dataDir);
|
|
69
|
+
expect(loaded.seededPlugins).toEqual(["substack"]);
|
|
70
|
+
await saveConfig(loaded);
|
|
71
|
+
expect((await loadConfig(dataDir)).seededPlugins).toEqual(["substack"]);
|
|
72
|
+
});
|
|
73
|
+
|
|
51
74
|
describe("sanitizeLayout", () => {
|
|
75
|
+
test("preserves an intentionally blank layout", () => {
|
|
76
|
+
expect(sanitizeLayout({
|
|
77
|
+
dockRoot: null,
|
|
78
|
+
instances: [],
|
|
79
|
+
floating: [],
|
|
80
|
+
detached: [],
|
|
81
|
+
}, DEFAULT_LAYOUT)).toEqual({
|
|
82
|
+
dockRoot: null,
|
|
83
|
+
instances: [],
|
|
84
|
+
floating: [],
|
|
85
|
+
detached: [],
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test("prunes abandoned panes while retaining a hidden follow source", () => {
|
|
90
|
+
const layout = sanitizeLayout({
|
|
91
|
+
dockRoot: { kind: "pane", instanceId: "ticker-research:visible" },
|
|
92
|
+
instances: [
|
|
93
|
+
{
|
|
94
|
+
instanceId: "portfolio-list:source",
|
|
95
|
+
paneId: "portfolio-list",
|
|
96
|
+
binding: { kind: "none" },
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
instanceId: "ticker-research:visible",
|
|
100
|
+
paneId: "ticker-research",
|
|
101
|
+
binding: { kind: "follow", sourceInstanceId: "portfolio-list:source" },
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
instanceId: "ticker-research:closed",
|
|
105
|
+
paneId: "ticker-research",
|
|
106
|
+
binding: { kind: "fixed", symbol: "NVDA" },
|
|
107
|
+
},
|
|
108
|
+
],
|
|
109
|
+
floating: [],
|
|
110
|
+
detached: [],
|
|
111
|
+
}, DEFAULT_LAYOUT);
|
|
112
|
+
|
|
113
|
+
expect(layout.instances.map((instance) => instance.instanceId)).toEqual([
|
|
114
|
+
"portfolio-list:source",
|
|
115
|
+
"ticker-research:visible",
|
|
116
|
+
]);
|
|
117
|
+
});
|
|
118
|
+
|
|
52
119
|
test("ships a focused Home layout with portfolio, chat, and following research", () => {
|
|
53
120
|
expect(DEFAULT_LAYOUT.instances.map((instance) => instance.instanceId)).toEqual([
|
|
54
121
|
"portfolio-list:main",
|
|
@@ -318,6 +385,59 @@ describe("sanitizeLayout", () => {
|
|
|
318
385
|
});
|
|
319
386
|
|
|
320
387
|
describe("loadConfig", () => {
|
|
388
|
+
test("migrates unreachable pane instances and their saved state", async () => {
|
|
389
|
+
const dataDir = await createTempConfigDir();
|
|
390
|
+
const hiddenPaneId = "ticker-research:closed";
|
|
391
|
+
const legacyLayout = {
|
|
392
|
+
...DEFAULT_LAYOUT,
|
|
393
|
+
instances: [
|
|
394
|
+
...DEFAULT_LAYOUT.instances.map((instance) => instance.instanceId === "chat:main"
|
|
395
|
+
? {
|
|
396
|
+
...instance,
|
|
397
|
+
placementMemory: {
|
|
398
|
+
docked: {
|
|
399
|
+
anchorInstanceId: hiddenPaneId,
|
|
400
|
+
position: "right" as const,
|
|
401
|
+
},
|
|
402
|
+
},
|
|
403
|
+
}
|
|
404
|
+
: instance),
|
|
405
|
+
{
|
|
406
|
+
instanceId: hiddenPaneId,
|
|
407
|
+
paneId: "ticker-research",
|
|
408
|
+
binding: { kind: "fixed" as const, symbol: "NVDA" },
|
|
409
|
+
placementMemory: { floating: { x: 12, y: 4, width: 90, height: 30 } },
|
|
410
|
+
},
|
|
411
|
+
],
|
|
412
|
+
};
|
|
413
|
+
await writeConfigJson(dataDir, createSavedConfig({
|
|
414
|
+
configVersion: 21,
|
|
415
|
+
layout: legacyLayout,
|
|
416
|
+
layouts: [{
|
|
417
|
+
name: "Default",
|
|
418
|
+
layout: legacyLayout,
|
|
419
|
+
paneState: {
|
|
420
|
+
"chat:main": { draft: "keep" },
|
|
421
|
+
[hiddenPaneId]: { activeTabId: "overview" },
|
|
422
|
+
},
|
|
423
|
+
focusedPaneId: hiddenPaneId,
|
|
424
|
+
}],
|
|
425
|
+
}));
|
|
426
|
+
|
|
427
|
+
const config = await loadConfig(dataDir);
|
|
428
|
+
|
|
429
|
+
expect(config.configVersion).toBe(CURRENT_CONFIG_VERSION);
|
|
430
|
+
expect(config.layout.instances.some((instance) => instance.instanceId === hiddenPaneId)).toBe(false);
|
|
431
|
+
expect(config.layouts[0]?.layout.instances.some((instance) => instance.instanceId === hiddenPaneId)).toBe(false);
|
|
432
|
+
expect(config.layouts[0]?.paneState).toEqual({ "chat:main": { draft: "keep" } });
|
|
433
|
+
expect(config.layouts[0]?.focusedPaneId).toBeNull();
|
|
434
|
+
expect(findPaneInstance(config.layout, "chat:main")?.placementMemory?.docked).toEqual({
|
|
435
|
+
anchorInstanceId: undefined,
|
|
436
|
+
path: undefined,
|
|
437
|
+
position: "right",
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
321
441
|
test("folds saved graph plugin state into composer specs and removes only chart-owned state", async () => {
|
|
322
442
|
const dataDir = await createTempConfigDir();
|
|
323
443
|
const legacyLayout = {
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
CURRENT_CONFIG_VERSION,
|
|
5
5
|
DEFAULT_COLUMNS,
|
|
6
6
|
DEFAULT_PORTFOLIO_COLUMN_IDS,
|
|
7
|
+
getPlacedPaneInstanceIds,
|
|
7
8
|
type LayoutConfig,
|
|
8
9
|
} from "../../../types/config";
|
|
9
10
|
import {
|
|
@@ -24,6 +25,7 @@ const CLOUD_MACRO_SPLIT_CONFIG_VERSION = 15;
|
|
|
24
25
|
const PORTFOLIO_DEFAULT_COLUMNS_CONFIG_VERSION = 17;
|
|
25
26
|
const BUILTIN_OWNERSHIP_AND_CHART_CONFIG_VERSION = 20;
|
|
26
27
|
const ONBOARDING_BACKFILL_CONFIG_VERSION = 21;
|
|
28
|
+
const UNREACHABLE_PANE_CLEANUP_CONFIG_VERSION = 22;
|
|
27
29
|
|
|
28
30
|
const LEGACY_MAIN_PORTFOLIO_COLUMN_IDS = DEFAULT_COLUMNS.map((column) => column.id);
|
|
29
31
|
const PRE_SPARKLINE_PORTFOLIO_COLUMN_IDS = [
|
|
@@ -85,6 +87,11 @@ const CONFIG_MIGRATIONS: readonly ConfigMigration[] = [
|
|
|
85
87
|
toVersion: ONBOARDING_BACKFILL_CONFIG_VERSION,
|
|
86
88
|
migrate: migrateOnboardingComplete,
|
|
87
89
|
},
|
|
90
|
+
{
|
|
91
|
+
name: "prune-unreachable-pane-instances",
|
|
92
|
+
toVersion: UNREACHABLE_PANE_CLEANUP_CONFIG_VERSION,
|
|
93
|
+
migrate: migrateUnreachablePaneInstances,
|
|
94
|
+
},
|
|
88
95
|
];
|
|
89
96
|
|
|
90
97
|
export function migrateSavedConfig(saved: Record<string, unknown>, dataDir: string): ConfigMigrationResult {
|
|
@@ -139,6 +146,40 @@ function migrateOnboardingComplete(saved: Record<string, unknown>): Record<strin
|
|
|
139
146
|
};
|
|
140
147
|
}
|
|
141
148
|
|
|
149
|
+
function migrateUnreachablePaneInstances(
|
|
150
|
+
saved: Record<string, unknown>,
|
|
151
|
+
dataDir: string,
|
|
152
|
+
): Record<string, unknown> {
|
|
153
|
+
const defaults = createDefaultConfig(dataDir);
|
|
154
|
+
const layout = sanitizeLayout(saved.layout, defaults.layout);
|
|
155
|
+
const layouts = Array.isArray(saved.layouts)
|
|
156
|
+
? saved.layouts.map((entry) => {
|
|
157
|
+
if (!isPlainRecord(entry) || typeof entry.name !== "string") return entry;
|
|
158
|
+
const entryLayout = sanitizeLayout(entry.layout, layout);
|
|
159
|
+
const instanceIds = new Set(entryLayout.instances.map((instance) => instance.instanceId));
|
|
160
|
+
const placedIds = new Set(getPlacedPaneInstanceIds(entryLayout));
|
|
161
|
+
const paneState = isPlainRecord(entry.paneState)
|
|
162
|
+
? Object.fromEntries(Object.entries(entry.paneState).filter(([instanceId]) => instanceIds.has(instanceId)))
|
|
163
|
+
: entry.paneState;
|
|
164
|
+
const focusedPaneId = typeof entry.focusedPaneId === "string" && !placedIds.has(entry.focusedPaneId)
|
|
165
|
+
? null
|
|
166
|
+
: entry.focusedPaneId;
|
|
167
|
+
return {
|
|
168
|
+
...entry,
|
|
169
|
+
layout: entryLayout,
|
|
170
|
+
paneState,
|
|
171
|
+
focusedPaneId,
|
|
172
|
+
};
|
|
173
|
+
})
|
|
174
|
+
: saved.layouts;
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
...saved,
|
|
178
|
+
layout,
|
|
179
|
+
layouts,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
|
|
142
183
|
function migrateCloudDefault(saved: Record<string, unknown>): Record<string, unknown> {
|
|
143
184
|
return {
|
|
144
185
|
...saved,
|
|
@@ -4,6 +4,7 @@ import { dirname, join } from "path";
|
|
|
4
4
|
import type { AppConfig } from "../../../types/config";
|
|
5
5
|
import { createDefaultConfig } from "../../../types/config";
|
|
6
6
|
import { debugLog } from "../../../utils/debug-log";
|
|
7
|
+
import { EXTRACTED_PLUGINS } from "../../../plugins/seed";
|
|
7
8
|
import {
|
|
8
9
|
normalizeConfigForSave,
|
|
9
10
|
normalizeLoadedConfig,
|
|
@@ -54,7 +55,12 @@ async function loadConfigState(dataDir: string): Promise<{ config: AppConfig; ne
|
|
|
54
55
|
const saved = JSON.parse(raw) as Record<string, unknown>;
|
|
55
56
|
return normalizeLoadedConfig(saved, dataDir);
|
|
56
57
|
} catch {
|
|
57
|
-
|
|
58
|
+
// Fresh installs have no former built-in plugins to restore. Record the
|
|
59
|
+
// current baseline so onboarding never waits for plugin network installs.
|
|
60
|
+
return {
|
|
61
|
+
config: { ...createDefaultConfig(dataDir), seededPlugins: EXTRACTED_PLUGINS.map((plugin) => plugin.id) },
|
|
62
|
+
needsSave: true,
|
|
63
|
+
};
|
|
58
64
|
}
|
|
59
65
|
}
|
|
60
66
|
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
cloneLayout,
|
|
11
11
|
createDefaultConfig,
|
|
12
12
|
CURRENT_CONFIG_VERSION,
|
|
13
|
+
getPlacedPaneInstanceIds,
|
|
13
14
|
} from "../../../types/config";
|
|
14
15
|
import type { Portfolio, Watchlist } from "../../../types/ticker";
|
|
15
16
|
import { isLanguagePreference } from "../../../i18n/languages";
|
|
@@ -51,6 +52,7 @@ export function normalizeLoadedConfig(saved: Record<string, unknown>, dataDir: s
|
|
|
51
52
|
activeLayoutIndex,
|
|
52
53
|
brokerInstances: sanitizeBrokerInstances(candidate.brokerInstances),
|
|
53
54
|
disabledPlugins,
|
|
55
|
+
seededPlugins: sanitizeUniqueStringList(candidate.seededPlugins),
|
|
54
56
|
disabledSources: sanitizeUniqueStringList(candidate.disabledSources ?? defaults.disabledSources),
|
|
55
57
|
pluginConfig: sanitizePluginConfig(candidate.pluginConfig),
|
|
56
58
|
theme: typeof candidate.theme === "string" ? candidate.theme : defaults.theme,
|
|
@@ -107,6 +109,7 @@ export function normalizeConfigForSave(config: AppConfig): AppConfig {
|
|
|
107
109
|
activeLayoutIndex,
|
|
108
110
|
brokerInstances: sanitizeBrokerInstances(config.brokerInstances),
|
|
109
111
|
disabledPlugins: sanitizeUniqueStringList(config.disabledPlugins),
|
|
112
|
+
seededPlugins: sanitizeUniqueStringList(config.seededPlugins),
|
|
110
113
|
disabledSources: sanitizeUniqueStringList(config.disabledSources),
|
|
111
114
|
pluginConfig: sanitizePluginConfig(config.pluginConfig),
|
|
112
115
|
chartPreferences: sanitizeChartPreferences(config.chartPreferences, defaults.chartPreferences),
|
|
@@ -129,6 +132,8 @@ const ONBOARDING_STAGES = new Set<OnboardingProgress["stage"]>([
|
|
|
129
132
|
"welcome",
|
|
130
133
|
"portfolio",
|
|
131
134
|
"add-ticker",
|
|
135
|
+
"research",
|
|
136
|
+
"verify",
|
|
132
137
|
"account",
|
|
133
138
|
"upgrade",
|
|
134
139
|
"ready",
|
|
@@ -305,15 +310,16 @@ function sanitizeSavedLayouts(
|
|
|
305
310
|
)
|
|
306
311
|
.map((entry) => {
|
|
307
312
|
const layout = sanitizeLayout(entry.layout, fallbackLayout);
|
|
313
|
+
const placedPaneIds = new Set(getPlacedPaneInstanceIds(layout));
|
|
308
314
|
const paneState = sanitizeSavedPaneState((entry as { paneState?: unknown }).paneState, layout);
|
|
309
315
|
return {
|
|
310
316
|
id: typeof entry.id === "string" ? entry.id : undefined,
|
|
311
317
|
name: entry.name,
|
|
312
318
|
layout,
|
|
313
319
|
paneState,
|
|
314
|
-
focusedPaneId: typeof entry.focusedPaneId === "string"
|
|
315
|
-
? entry.focusedPaneId
|
|
316
|
-
: undefined,
|
|
320
|
+
focusedPaneId: typeof entry.focusedPaneId === "string"
|
|
321
|
+
? placedPaneIds.has(entry.focusedPaneId) ? entry.focusedPaneId : null
|
|
322
|
+
: entry.focusedPaneId === null ? null : undefined,
|
|
317
323
|
activePanel: entry.activePanel === "right" || entry.activePanel === "left"
|
|
318
324
|
? entry.activePanel
|
|
319
325
|
: undefined,
|
package/src/i18n/es.ts
CHANGED
|
@@ -572,6 +572,7 @@ export const es: Record<string, string> = {
|
|
|
572
572
|
"Open the help window": "Abrir la ventana de ayuda",
|
|
573
573
|
"Set up a portfolio": "Configura una cartera",
|
|
574
574
|
"How would you like to get started?": "¿Cómo quieres empezar?",
|
|
575
|
+
"{shortcut} to get started.": "{shortcut} para empezar.",
|
|
575
576
|
"Edits later from the command bar.": "Se edita luego desde la barra de comandos.",
|
|
576
577
|
"Create Manual Portfolio": "Crear cartera manual",
|
|
577
578
|
"Add tickers and positions by hand": "Agrega tickers y posiciones a mano",
|
|
@@ -760,7 +761,7 @@ export const es: Record<string, string> = {
|
|
|
760
761
|
"Verify your email to send messages.": "Verifica tu correo para enviar mensajes.",
|
|
761
762
|
"Verify your email to use Cloud tweets.": "Verifica tu correo para usar Cloud tweets.",
|
|
762
763
|
"Resend Verification Email": "Reenviar correo de verificación",
|
|
763
|
-
"
|
|
764
|
+
"{shortcut}, then Resend Verification Email": "{shortcut} y luego Reenviar correo de verificación",
|
|
764
765
|
"editing": "editando",
|
|
765
766
|
"replying to": "respondiendo a",
|
|
766
767
|
"Reply": "Responder",
|
|
@@ -998,7 +999,7 @@ export const es: Record<string, string> = {
|
|
|
998
999
|
"No watchlist tickers": "Sin tickers en seguimiento",
|
|
999
1000
|
"No tickers in {name}.": "No hay tickers en {name}.",
|
|
1000
1001
|
"this collection": "esta colección",
|
|
1001
|
-
"Press
|
|
1002
|
+
"Press {shortcut} to add one.": "Pulsa {shortcut} para agregar uno.",
|
|
1002
1003
|
"PRE-MKT": "PRE-MKT",
|
|
1003
1004
|
"OPEN": "ABIERTO",
|
|
1004
1005
|
"AFTER-HRS": "POST-MKT",
|
|
@@ -1092,6 +1093,9 @@ export const es: Record<string, string> = {
|
|
|
1092
1093
|
"Approve from your phone, no typing": "Aprueba desde tu teléfono, sin escribir",
|
|
1093
1094
|
"Scan with the Gloom app": "Escanea con la app Gloom",
|
|
1094
1095
|
"Open the Gloom app on your phone and approve this workspace.": "Abre la app Gloom en tu teléfono y aprueba este espacio de trabajo.",
|
|
1096
|
+
"Approve the code from a phone that is already signed in.": "Aprueba el código desde un teléfono que ya tenga la sesión iniciada.",
|
|
1097
|
+
"Layouts, watchlists and portfolios sync to your account.": "Los diseños, las listas de seguimiento y las carteras se sincronizan con tu cuenta.",
|
|
1098
|
+
"Use email instead": "Usar el correo",
|
|
1095
1099
|
"Scan with the Gloom app to sign in": "Escanea con la app Gloom para iniciar sesión",
|
|
1096
1100
|
"Waiting for approval...": "Esperando aprobación...",
|
|
1097
1101
|
"Approved as {email}": "Aprobado como {email}",
|
package/src/i18n/ja.ts
CHANGED
|
@@ -573,6 +573,7 @@ export const ja: Record<string, string> = {
|
|
|
573
573
|
"Open the help window": "ヘルプ ウィンドウを開く",
|
|
574
574
|
"Set up a portfolio": "ポートフォリオを設定する",
|
|
575
575
|
"How would you like to get started?": "どの方法で始めますか?",
|
|
576
|
+
"{shortcut} to get started.": "{shortcut} で開始します。",
|
|
576
577
|
"Edits later from the command bar.": "後でコマンド バーから編集します。",
|
|
577
578
|
"Create Manual Portfolio": "手動ポートフォリオを作成",
|
|
578
579
|
"Add tickers and positions by hand": "銘柄とポジションを手動で追加",
|
|
@@ -775,7 +776,7 @@ export const ja: Record<string, string> = {
|
|
|
775
776
|
"Verify your email to send messages.": "メッセージを送信するにはメールを確認してください。",
|
|
776
777
|
"Verify your email to use Cloud tweets.": "Cloud tweets を使用するにはメールを確認してください。",
|
|
777
778
|
"Resend Verification Email": "確認メールを再送信",
|
|
778
|
-
"
|
|
779
|
+
"{shortcut}, then Resend Verification Email": "{shortcut} から「確認メールを再送信」",
|
|
779
780
|
"editing": "編集",
|
|
780
781
|
"replying to": "返信先",
|
|
781
782
|
"Reply": "返信",
|
|
@@ -1013,7 +1014,7 @@ export const ja: Record<string, string> = {
|
|
|
1013
1014
|
"No watchlist tickers": "ウォッチリストに銘柄がありません",
|
|
1014
1015
|
"No tickers in {name}.": "{name} に銘柄がありません。",
|
|
1015
1016
|
"this collection": "このコレクション",
|
|
1016
|
-
"Press
|
|
1017
|
+
"Press {shortcut} to add one.": "{shortcut} を押して追加します。",
|
|
1017
1018
|
"PRE-MKT": "PRE-MKT",
|
|
1018
1019
|
"OPEN": "取引中",
|
|
1019
1020
|
"AFTER-HRS": "時間外",
|
|
@@ -1079,6 +1080,9 @@ export const ja: Record<string, string> = {
|
|
|
1079
1080
|
"Approve from your phone, no typing": "スマホから承認、入力不要",
|
|
1080
1081
|
"Scan with the Gloom app": "Gloom アプリでスキャン",
|
|
1081
1082
|
"Open the Gloom app on your phone and approve this workspace.": "スマホで Gloom アプリを開き、このワークスペースを承認してください。",
|
|
1083
|
+
"Approve the code from a phone that is already signed in.": "すでにログイン済みのスマートフォンでコードを承認してください。",
|
|
1084
|
+
"Layouts, watchlists and portfolios sync to your account.": "レイアウト、ウォッチリスト、ポートフォリオがアカウントに同期されます。",
|
|
1085
|
+
"Use email instead": "メールを使う",
|
|
1082
1086
|
"Scan with the Gloom app to sign in": "Gloom アプリでスキャンしてログイン",
|
|
1083
1087
|
"Waiting for approval...": "承認を待っています...",
|
|
1084
1088
|
"Approved as {email}": "{email} として承認されました",
|
package/src/i18n/ko.ts
CHANGED
|
@@ -571,6 +571,7 @@ export const ko: Record<string, string> = {
|
|
|
571
571
|
"Open the help window": "도움말 창 열기",
|
|
572
572
|
"Set up a portfolio": "포트폴리오 설정",
|
|
573
573
|
"How would you like to get started?": "어떻게 시작하시겠습니까?",
|
|
574
|
+
"{shortcut} to get started.": "{shortcut} 로 시작하세요.",
|
|
574
575
|
"Edits later from the command bar.": "나중에 명령 팔레트에서 수정할 수 있습니다.",
|
|
575
576
|
"Create Manual Portfolio": "수동 포트폴리오 만들기",
|
|
576
577
|
"Add tickers and positions by hand": "종목과 포지션 직접 추가",
|
|
@@ -773,7 +774,7 @@ export const ko: Record<string, string> = {
|
|
|
773
774
|
"Verify your email to send messages.": "메시지를 보내려면 이메일을 인증하세요.",
|
|
774
775
|
"Verify your email to use Cloud tweets.": "Cloud 게시물을 사용하려면 이메일을 인증하세요.",
|
|
775
776
|
"Resend Verification Email": "확인 이메일 다시 보내기",
|
|
776
|
-
"
|
|
777
|
+
"{shortcut}, then Resend Verification Email": "{shortcut} 후 인증 이메일 재전송",
|
|
777
778
|
"editing": "편집 중",
|
|
778
779
|
"replying to": "답장 대상",
|
|
779
780
|
"Reply": "답장",
|
|
@@ -1011,7 +1012,7 @@ export const ko: Record<string, string> = {
|
|
|
1011
1012
|
"No watchlist tickers": "관심 목록에 종목 없음",
|
|
1012
1013
|
"No tickers in {name}.": "{name}에 종목이 없습니다.",
|
|
1013
1014
|
"this collection": "이 컬렉션",
|
|
1014
|
-
"Press
|
|
1015
|
+
"Press {shortcut} to add one.": "{shortcut} 를 눌러 추가하세요.",
|
|
1015
1016
|
"PRE-MKT": "장전",
|
|
1016
1017
|
"OPEN": "장중",
|
|
1017
1018
|
"AFTER-HRS": "장후",
|
|
@@ -1077,6 +1078,9 @@ export const ko: Record<string, string> = {
|
|
|
1077
1078
|
"Approve from your phone, no typing": "입력 없이 휴대폰에서 승인",
|
|
1078
1079
|
"Scan with the Gloom app": "Gloom 앱으로 스캔",
|
|
1079
1080
|
"Open the Gloom app on your phone and approve this workspace.": "휴대폰에서 Gloom 앱을 열고 이 워크스페이스를 승인하세요.",
|
|
1081
|
+
"Approve the code from a phone that is already signed in.": "이미 로그인한 휴대폰에서 코드를 승인하세요.",
|
|
1082
|
+
"Layouts, watchlists and portfolios sync to your account.": "레이아웃, 관심 목록, 포트폴리오가 계정에 동기화됩니다.",
|
|
1083
|
+
"Use email instead": "이메일 사용",
|
|
1080
1084
|
"Scan with the Gloom app to sign in": "Gloom 앱으로 스캔하여 로그인",
|
|
1081
1085
|
"Waiting for approval...": "승인을 기다리는 중...",
|
|
1082
1086
|
"Approved as {email}": "{email}(으)로 승인됨",
|
package/src/i18n/zh-cn.ts
CHANGED
|
@@ -573,6 +573,7 @@ export const zhCN: Record<string, string> = {
|
|
|
573
573
|
"Open the help window": "打开帮助窗口",
|
|
574
574
|
"Set up a portfolio": "设置投资组合",
|
|
575
575
|
"How would you like to get started?": "你想如何开始?",
|
|
576
|
+
"{shortcut} to get started.": "按 {shortcut} 开始。",
|
|
576
577
|
"Edits later from the command bar.": "之后可在命令栏中修改。",
|
|
577
578
|
"Create Manual Portfolio": "创建手动组合",
|
|
578
579
|
"Add tickers and positions by hand": "手动添加股票与持仓",
|
|
@@ -775,7 +776,7 @@ export const zhCN: Record<string, string> = {
|
|
|
775
776
|
"Verify your email to send messages.": "验证邮箱后才能发送消息。",
|
|
776
777
|
"Verify your email to use Cloud tweets.": "验证邮箱后才能使用 Cloud tweets。",
|
|
777
778
|
"Resend Verification Email": "重新发送验证邮件",
|
|
778
|
-
"
|
|
779
|
+
"{shortcut}, then Resend Verification Email": "按 {shortcut},然后选择“重新发送验证邮件”",
|
|
779
780
|
"editing": "编辑中",
|
|
780
781
|
"replying to": "回复",
|
|
781
782
|
"Reply": "回复",
|
|
@@ -1013,7 +1014,7 @@ export const zhCN: Record<string, string> = {
|
|
|
1013
1014
|
"No watchlist tickers": "自选列表暂无股票",
|
|
1014
1015
|
"No tickers in {name}.": "{name} 中暂无股票。",
|
|
1015
1016
|
"this collection": "此集合",
|
|
1016
|
-
"Press
|
|
1017
|
+
"Press {shortcut} to add one.": "按 {shortcut} 添加。",
|
|
1017
1018
|
"PRE-MKT": "盘前",
|
|
1018
1019
|
"OPEN": "盘中",
|
|
1019
1020
|
"AFTER-HRS": "盘后",
|
|
@@ -1079,6 +1080,9 @@ export const zhCN: Record<string, string> = {
|
|
|
1079
1080
|
"Approve from your phone, no typing": "在手机上确认,无需输入",
|
|
1080
1081
|
"Scan with the Gloom app": "用 Gloom 应用扫码",
|
|
1081
1082
|
"Open the Gloom app on your phone and approve this workspace.": "在手机上打开 Gloom 应用并确认此工作区。",
|
|
1083
|
+
"Approve the code from a phone that is already signed in.": "在已登录的手机上确认此代码。",
|
|
1084
|
+
"Layouts, watchlists and portfolios sync to your account.": "布局、自选列表和投资组合会同步到你的账户。",
|
|
1085
|
+
"Use email instead": "改用邮箱",
|
|
1082
1086
|
"Scan with the Gloom app to sign in": "用 Gloom 应用扫码登录",
|
|
1083
1087
|
"Waiting for approval...": "等待确认...",
|
|
1084
1088
|
"Approved as {email}": "已以 {email} 登录",
|
package/src/i18n/zh-tw.ts
CHANGED
|
@@ -573,6 +573,7 @@ export const zhTW: Record<string, string> = {
|
|
|
573
573
|
"Open the help window": "開啟說明視窗",
|
|
574
574
|
"Set up a portfolio": "設定投資組合",
|
|
575
575
|
"How would you like to get started?": "你想如何開始?",
|
|
576
|
+
"{shortcut} to get started.": "按 {shortcut} 開始。",
|
|
576
577
|
"Edits later from the command bar.": "之後可在命令列中修改。",
|
|
577
578
|
"Create Manual Portfolio": "建立手動投資組合",
|
|
578
579
|
"Add tickers and positions by hand": "手動新增股票與持倉",
|
|
@@ -775,7 +776,7 @@ export const zhTW: Record<string, string> = {
|
|
|
775
776
|
"Verify your email to send messages.": "驗證電子郵件後才能傳送訊息。",
|
|
776
777
|
"Verify your email to use Cloud tweets.": "驗證電子郵件後才能使用 Cloud tweets。",
|
|
777
778
|
"Resend Verification Email": "重新發送驗證郵件",
|
|
778
|
-
"
|
|
779
|
+
"{shortcut}, then Resend Verification Email": "按 {shortcut},然後選擇「重新發送驗證郵件」",
|
|
779
780
|
"editing": "編輯中",
|
|
780
781
|
"replying to": "回覆",
|
|
781
782
|
"Reply": "回覆",
|
|
@@ -1013,7 +1014,7 @@ export const zhTW: Record<string, string> = {
|
|
|
1013
1014
|
"No watchlist tickers": "自選清單暫無股票",
|
|
1014
1015
|
"No tickers in {name}.": "{name} 中暫無股票。",
|
|
1015
1016
|
"this collection": "此清單",
|
|
1016
|
-
"Press
|
|
1017
|
+
"Press {shortcut} to add one.": "按 {shortcut} 新增。",
|
|
1017
1018
|
"PRE-MKT": "盤前",
|
|
1018
1019
|
"OPEN": "盤中",
|
|
1019
1020
|
"AFTER-HRS": "盤後",
|
|
@@ -1079,6 +1080,9 @@ export const zhTW: Record<string, string> = {
|
|
|
1079
1080
|
"Approve from your phone, no typing": "在手機上確認,無需輸入",
|
|
1080
1081
|
"Scan with the Gloom app": "用 Gloom 應用程式掃碼",
|
|
1081
1082
|
"Open the Gloom app on your phone and approve this workspace.": "在手機上開啟 Gloom 應用程式並確認此工作區。",
|
|
1083
|
+
"Approve the code from a phone that is already signed in.": "在已登入的手機上確認這組代碼。",
|
|
1084
|
+
"Layouts, watchlists and portfolios sync to your account.": "版面、自選清單與投資組合會同步到你的帳號。",
|
|
1085
|
+
"Use email instead": "改用電子郵件",
|
|
1082
1086
|
"Scan with the Gloom app to sign in": "用 Gloom 應用程式掃碼登入",
|
|
1083
1087
|
"Waiting for approval...": "等待確認...",
|
|
1084
1088
|
"Approved as {email}": "已以 {email} 登入",
|
|
@@ -157,6 +157,23 @@ describe("layout marketplace payloads", () => {
|
|
|
157
157
|
expect(JSON.stringify(pane)).not.toContain("secret");
|
|
158
158
|
});
|
|
159
159
|
|
|
160
|
+
test("drops cache-sized pane state instead of failing the share", () => {
|
|
161
|
+
const pane = publishableMarketplacePane({
|
|
162
|
+
instanceId: "news-top:main",
|
|
163
|
+
paneId: "prediction-markets",
|
|
164
|
+
binding: { kind: "none" },
|
|
165
|
+
}, {
|
|
166
|
+
sort: { columnId: "time", direction: "desc" },
|
|
167
|
+
"news-top:articles": Array.from({ length: 400 }, (_, index) => ({
|
|
168
|
+
id: `article-${index}`,
|
|
169
|
+
title: "x".repeat(200),
|
|
170
|
+
publishedAt: "2026-08-26T00:00:00.000Z",
|
|
171
|
+
})),
|
|
172
|
+
}, panes);
|
|
173
|
+
|
|
174
|
+
expect(pane.paneState.p1).toEqual({ sort: { columnId: "time", direction: "desc" } });
|
|
175
|
+
});
|
|
176
|
+
|
|
160
177
|
test("shares portable pane setup and state while redacting private data", () => {
|
|
161
178
|
const fixture = portableFixture();
|
|
162
179
|
const payload = publishableMarketplaceLayout(fixture.layout, fixture.paneState, panes);
|
|
@@ -12,6 +12,10 @@ import type {
|
|
|
12
12
|
} from "../types/plugin";
|
|
13
13
|
|
|
14
14
|
const MAX_LAYOUT_BYTES = 128 * 1024;
|
|
15
|
+
// Pane runtime state doubles as a cache (news panes keep six figures of bytes of
|
|
16
|
+
// article backlog), so bulky fields drop out of the share rather than failing
|
|
17
|
+
// the whole publish.
|
|
18
|
+
const MAX_PANE_STATE_FIELD_BYTES = 16 * 1024;
|
|
15
19
|
const MAX_INSTANCES = 40;
|
|
16
20
|
const MAX_DOCK_DEPTH = 20;
|
|
17
21
|
const MAX_ID_LENGTH = 160;
|
|
@@ -493,14 +497,45 @@ function publishableLayout(
|
|
|
493
497
|
height,
|
|
494
498
|
})),
|
|
495
499
|
};
|
|
496
|
-
|
|
500
|
+
let publishableState = Object.fromEntries(
|
|
501
|
+
Object.entries(projectedState).flatMap(([id, state]) => {
|
|
502
|
+
const trimmed = withoutCacheFields(state);
|
|
503
|
+
return Object.keys(trimmed).length > 0 ? [[id, trimmed] as const] : [];
|
|
504
|
+
}),
|
|
505
|
+
);
|
|
506
|
+
let parsed = parsePublishablePayload(projected, publishableState);
|
|
507
|
+
while (!parsed && Object.keys(publishableState).length > 0) {
|
|
508
|
+
publishableState = withoutLargestState(publishableState);
|
|
509
|
+
parsed = parsePublishablePayload(projected, publishableState);
|
|
510
|
+
}
|
|
511
|
+
if (!parsed) throw new Error("This layout cannot be published safely.");
|
|
512
|
+
return parsed;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
function parsePublishablePayload(
|
|
516
|
+
layout: LayoutConfig,
|
|
517
|
+
paneState: Record<string, PaneRuntimeState>,
|
|
518
|
+
): LayoutMarketplacePayload | null {
|
|
519
|
+
return parseMarketplaceLayoutPayload({
|
|
497
520
|
schemaVersion: 2,
|
|
498
521
|
sourceConfigVersion: CURRENT_CONFIG_VERSION,
|
|
499
|
-
layout
|
|
500
|
-
paneState
|
|
522
|
+
layout,
|
|
523
|
+
paneState,
|
|
501
524
|
});
|
|
502
|
-
|
|
503
|
-
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function withoutCacheFields(state: PaneRuntimeState): PaneRuntimeState {
|
|
528
|
+
return Object.fromEntries(
|
|
529
|
+
Object.entries(state).filter(([, value]) => encodedSize(value) <= MAX_PANE_STATE_FIELD_BYTES),
|
|
530
|
+
) as PaneRuntimeState;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
function withoutLargestState(
|
|
534
|
+
paneState: Record<string, PaneRuntimeState>,
|
|
535
|
+
): Record<string, PaneRuntimeState> {
|
|
536
|
+
const largest = Object.entries(paneState)
|
|
537
|
+
.reduce((worst, entry) => (encodedSize(entry[1]) > encodedSize(worst[1]) ? entry : worst));
|
|
538
|
+
return Object.fromEntries(Object.entries(paneState).filter(([id]) => id !== largest[0]));
|
|
504
539
|
}
|
|
505
540
|
|
|
506
541
|
export function publishableMarketplaceLayout(
|
|
@@ -55,7 +55,8 @@ import {
|
|
|
55
55
|
loadQuoteBatchEntries,
|
|
56
56
|
loadQuoteEntry,
|
|
57
57
|
QuoteSubscriptionManager,
|
|
58
|
-
type
|
|
58
|
+
type QuoteSubscriptionHandle,
|
|
59
|
+
type QuoteSubscriptionRequest,
|
|
59
60
|
} from "./quotes";
|
|
60
61
|
|
|
61
62
|
export class MarketDataCoordinator {
|
|
@@ -361,7 +362,7 @@ export class MarketDataCoordinator {
|
|
|
361
362
|
});
|
|
362
363
|
}
|
|
363
364
|
|
|
364
|
-
subscribeQuotes(targets:
|
|
365
|
+
subscribeQuotes(targets: QuoteSubscriptionRequest[]): QuoteSubscriptionHandle {
|
|
365
366
|
return this.quoteSubscriptionManager.subscribe(targets);
|
|
366
367
|
}
|
|
367
368
|
|