gloomberb 0.11.2 → 0.12.0
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 +7 -4
- package/README.zh-CN.md +4 -2
- package/package.json +28 -1
- package/src/api-client/auth.ts +10 -0
- package/src/api-client/data.ts +108 -1
- package/src/api-client/index.test.ts +127 -0
- package/src/api-client/index.ts +134 -1
- package/src/api-client/normalizers.ts +39 -0
- package/src/api-client/paths.ts +85 -1
- package/src/api-client/types.ts +220 -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/capabilities/chart-series.ts +3 -2
- package/src/cli/commands/market.ts +2 -2
- package/src/cli/commands/plugins.ts +39 -11
- package/src/cli/desktop-pane-shot.ts +285 -16
- package/src/cli/index.ts +1 -1
- package/src/cli/pane-functions/capabilities.ts +61 -31
- package/src/cli/pane-functions/catalog.ts +16 -4
- package/src/cli/pane-functions/discovery.ts +1 -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 +9 -0
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +27 -2
- package/src/cli/pane-functions/report.ts +4 -0
- 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 +236 -20
- package/src/cli/registry.ts +0 -22
- package/src/cli/restore-plugins.ts +34 -0
- package/src/cli/result.ts +7 -10
- 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 +68 -20
- package/src/components/chart/composite/composite-chart.tsx +301 -204
- 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/scene.ts +57 -3
- package/src/components/chart/composite/text-renderer.ts +3 -2
- package/src/components/chart/composite/time-scale.ts +138 -57
- package/src/components/chart/composite/types.ts +25 -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 +14 -10
- 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 +15 -3
- 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/export.test.ts +21 -0
- package/src/components/data-table/export.ts +21 -0
- 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/onboarding-frame.tsx +2 -26
- 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 +22 -4
- 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/chart-settings.ts +4 -4
- package/src/data/config/layout.ts +12 -6
- package/src/data/config/store/index.test.ts +97 -0
- package/src/data/config/store/migrations.ts +41 -0
- package/src/data/config/store/normalize.ts +5 -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/gallery-terminal.tsx +126 -96
- package/src/layout-marketplace/gallery.test.tsx +65 -1
- 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/chart-composer/catalog-inventory.ts +16 -2
- package/src/plugins/builtin/chart-composer/cli-options.ts +12 -9
- package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
- package/src/plugins/builtin/chart-composer/settings.ts +2 -13
- package/src/plugins/builtin/chat/content/composer.tsx +8 -4
- package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
- package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
- package/src/plugins/builtin/cloud/plugin.tsx +1 -0
- package/src/plugins/builtin/composite-plugins.ts +16 -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/index.test.tsx +12 -6
- package/src/plugins/builtin/credit-conditions/index.tsx +2 -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/index.tsx +1 -0
- 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 +756 -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 +334 -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 +3 -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/futures/index.tsx +1 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +1 -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/ipo-calendar/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-halts/index.tsx +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +1 -0
- 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/analytics.test.ts +83 -0
- package/src/plugins/builtin/options/analytics.ts +110 -0
- package/src/plugins/builtin/options/index.tsx +18 -2
- package/src/plugins/builtin/options/table.ts +121 -51
- package/src/plugins/builtin/options/types.ts +22 -15
- package/src/plugins/builtin/options/view.test.tsx +53 -1
- package/src/plugins/builtin/options/view.tsx +92 -41
- 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.test.ts +2 -4
- package/src/plugins/builtin/plugin-module.ts +9 -8
- package/src/plugins/builtin/portfolio-list/index.tsx +1 -0
- package/src/plugins/builtin/research/index.tsx +4 -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 +678 -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/index.ts +2 -0
- package/src/plugins/builtin/sec/index.tsx +1 -0
- package/src/plugins/builtin/sectors/index.tsx +1 -0
- 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/data-panes/historical-prices.tsx +3 -5
- package/src/plugins/builtin/ticker-detail/index.tsx +2 -14
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
- package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
- package/src/plugins/builtin/volatility/index.tsx +1 -1
- package/src/plugins/builtin/world-indices/index.tsx +1 -0
- package/src/plugins/builtin/world-venue-map/index.ts +27 -0
- package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +481 -0
- package/src/plugins/builtin/world-venue-map/model.test.ts +85 -0
- package/src/plugins/builtin/world-venue-map/model.ts +268 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +300 -0
- package/src/plugins/builtin/world-venue-map/world-outlines.ts +2 -0
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/bundle.test.ts +109 -0
- package/src/plugins/bundle.ts +139 -0
- package/src/plugins/catalog-backend.ts +6 -44
- package/src/plugins/catalog-browser.test.ts +1 -0
- package/src/plugins/catalog-browser.ts +12 -0
- package/src/plugins/catalog-manifest.test.ts +43 -0
- package/src/plugins/catalog-manifest.ts +92 -0
- package/src/plugins/catalog-ui.ts +20 -4
- package/src/plugins/catalog.ts +9 -2
- package/src/plugins/current-target.ts +28 -0
- package/src/plugins/host-contract.ts +37 -0
- package/src/plugins/host-link.ts +158 -0
- package/src/plugins/host-modules.ts +61 -0
- package/src/plugins/loader.ts +41 -22
- 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.test.ts +53 -0
- package/src/plugins/registry/pane-settings.ts +29 -3
- package/src/plugins/seed.test.ts +83 -0
- package/src/plugins/seed.ts +80 -0
- package/src/public/broker.ts +31 -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.ts +1 -0
- package/src/remote/semantic-tree.test.tsx +105 -0
- package/src/remote/semantic-tree.tsx +53 -35
- package/src/renderers/browser/main.tsx +6 -0
- package/src/renderers/browser/ui-host.tsx +11 -0
- package/src/renderers/cloudflare/worker.test.ts +19 -0
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -2
- package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
- package/src/renderers/electrobun/bun/index.ts +6 -2
- package/src/renderers/electrobun/shared/protocol.ts +25 -0
- package/src/renderers/electrobun/view/build-assets.ts +0 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
- 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/electrobun/view/ui-host.tsx +3 -0
- package/src/renderers/opentui/chart-surface.tsx +5 -1
- package/src/renderers/opentui/host.tsx +20 -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 +50 -0
- 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/range.ts +12 -0
- package/src/time-series/resolution.test.ts +25 -26
- package/src/time-series/resolution.ts +44 -25
- package/src/time-series/resolve.ts +7 -0
- package/src/time-series/spec.ts +5 -5
- package/src/time-series/types.ts +3 -1
- package/src/time-series/use-chart-resolution.ts +5 -0
- package/src/types/config.ts +123 -3
- package/src/types/data-provider.ts +11 -0
- package/src/types/headless.ts +161 -0
- package/src/types/plugin.ts +104 -20
- package/src/ui/host.tsx +19 -1
- package/src/ui/index.tsx +1 -0
- package/src/ui/toast.tsx +8 -4
- package/src/utils/csv.test.ts +13 -0
- package/src/utils/csv.ts +37 -0
- 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/save-text-file.test.ts +24 -0
- package/src/utils/save-text-file.ts +36 -0
- 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/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
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { BrokerAdapter } from "../types/broker";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A stand-in broker for tests of the generic broker machinery.
|
|
5
|
+
*
|
|
6
|
+
* These tests used to reach for the real IBKR adapter, which now lives in its
|
|
7
|
+
* own repository. Depending on a plugin to exercise host code is backwards
|
|
8
|
+
* anyway: it made a core test fail whenever that plugin's schema changed, and
|
|
9
|
+
* it hid which behaviour was actually under test.
|
|
10
|
+
*
|
|
11
|
+
* The shape mirrors what the machinery has to handle — a mode switch, a nested
|
|
12
|
+
* config, and a password field that must survive an edit left blank.
|
|
13
|
+
*/
|
|
14
|
+
export const testBroker: BrokerAdapter = {
|
|
15
|
+
id: "test-broker",
|
|
16
|
+
name: "Test Broker",
|
|
17
|
+
configSchema: [
|
|
18
|
+
{
|
|
19
|
+
key: "connectionMode",
|
|
20
|
+
label: "Connection",
|
|
21
|
+
type: "select",
|
|
22
|
+
options: [
|
|
23
|
+
{ value: "token", label: "Token" },
|
|
24
|
+
{ value: "local", label: "Local" },
|
|
25
|
+
],
|
|
26
|
+
defaultValue: "token",
|
|
27
|
+
},
|
|
28
|
+
{ key: "token", label: "API Token", type: "password", required: true, visibleWhen: { connectionMode: "token" } },
|
|
29
|
+
{ key: "accountId", label: "Account", type: "text", required: true, visibleWhen: { connectionMode: "token" } },
|
|
30
|
+
{ key: "host", label: "Host", type: "text", defaultValue: "127.0.0.1", visibleWhen: { connectionMode: "local" } },
|
|
31
|
+
],
|
|
32
|
+
|
|
33
|
+
async validate() {
|
|
34
|
+
return true;
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
async importPositions() {
|
|
38
|
+
return [];
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
getStatus() {
|
|
42
|
+
return { state: "disconnected", message: "", updatedAt: 0 };
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
getProfileActions() {
|
|
46
|
+
// The pane renders and invokes profile actions; one is enough to cover that.
|
|
47
|
+
return [{ id: "test-console", label: "Console", paneId: "ibkr-trading" }];
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
toConfigValues(instance) {
|
|
51
|
+
const config = instance.config as Record<string, unknown>;
|
|
52
|
+
const credentials = (config.credentials ?? {}) as Record<string, unknown>;
|
|
53
|
+
return {
|
|
54
|
+
connectionMode: String(config.connectionMode ?? "token"),
|
|
55
|
+
token: String(credentials.token ?? ""),
|
|
56
|
+
accountId: String(credentials.accountId ?? ""),
|
|
57
|
+
host: String(config.host ?? "127.0.0.1"),
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
fromConfigValues(values) {
|
|
62
|
+
return {
|
|
63
|
+
connectionMode: values.connectionMode,
|
|
64
|
+
credentials: { token: values.token, accountId: values.accountId },
|
|
65
|
+
host: values.host,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -9,6 +9,7 @@ import type {
|
|
|
9
9
|
SeriesTransform,
|
|
10
10
|
TimeSeriesPoint,
|
|
11
11
|
} from "../time-series/types";
|
|
12
|
+
import { CHART_RESOLUTIONS, TIME_RANGES } from "../time-series/range";
|
|
12
13
|
import type {
|
|
13
14
|
CapabilityInvoker,
|
|
14
15
|
CapabilitySchema,
|
|
@@ -24,8 +25,8 @@ export const MAX_CHART_SERIES_POINTS = 20_000;
|
|
|
24
25
|
const CAPABILITY_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/;
|
|
25
26
|
const SERIES_ID_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._~%:/-]{0,239}$/;
|
|
26
27
|
const chartSeriesLog = debugLog.createLogger("chart-series");
|
|
27
|
-
const ranges = new Set(
|
|
28
|
-
const resolutions = new Set(
|
|
28
|
+
const ranges = new Set<string>(TIME_RANGES);
|
|
29
|
+
const resolutions = new Set<string>(CHART_RESOLUTIONS);
|
|
29
30
|
const periods = new Set(["auto", "daily", "weekly", "monthly", "quarterly", "annual", "ttm"]);
|
|
30
31
|
const styles = new Set(["line", "area", "step", "columns", "points", "candles", "ohlc", "hlc"]);
|
|
31
32
|
const transforms = new Set(["raw", "percent", "index100", "yoy", "qoq", "log"]);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CliCommandDef } from "../../types/plugin";
|
|
2
|
-
import type
|
|
2
|
+
import { TIME_RANGES, type TimeRange } from "../../time-series/range";
|
|
3
3
|
import type { EarningsEvent, QuoteBatchResult, SecFilingItem } from "../../types/data-provider";
|
|
4
4
|
import type { NewsArticle, NewsFeed, NewsQuery } from "../../news/types";
|
|
5
5
|
import type {
|
|
@@ -15,7 +15,7 @@ import { formatCompact } from "../../utils/format";
|
|
|
15
15
|
import { withCliServices, withMarketData } from "../context";
|
|
16
16
|
import { isoDate, parsePositiveInt, requireArg, takeOption } from "./command-utils";
|
|
17
17
|
|
|
18
|
-
const VALID_RANGES = new Set<TimeRange>(
|
|
18
|
+
const VALID_RANGES = new Set<TimeRange>(TIME_RANGES);
|
|
19
19
|
const VALID_NEWS_FEEDS = new Set<NewsFeed>(["latest", "top", "breaking", "ticker", "sector", "topic"]);
|
|
20
20
|
|
|
21
21
|
type QuoteCliRecord = Omit<QuoteBatchResult, "error"> & { error: string | null };
|
|
@@ -2,6 +2,7 @@ import { join } from "path";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "fs";
|
|
3
3
|
import { execFileSync } from "child_process";
|
|
4
4
|
import { getPluginsDir } from "../../plugins/loader";
|
|
5
|
+
import { linkHostPackages } from "../../plugins/host-link";
|
|
5
6
|
import {
|
|
6
7
|
cliStyles,
|
|
7
8
|
renderSection,
|
|
@@ -50,7 +51,20 @@ function parseGitHubRef(rawRef: string): { url: string; name: string } {
|
|
|
50
51
|
throw new Error(`Invalid plugin reference: ${ref}. Use user/repo or a GitHub URL.`);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
export
|
|
54
|
+
export interface InstallPluginOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Suppress child-process output and progress logging. The marketplace pane
|
|
57
|
+
* installs while the terminal UI owns the screen, and git and bun writing to
|
|
58
|
+
* stdout corrupts the rendered frame.
|
|
59
|
+
*/
|
|
60
|
+
quiet?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function installPlugin(ref: string, options: InstallPluginOptions = {}) {
|
|
64
|
+
const stdio = options.quiet ? "pipe" : "inherit";
|
|
65
|
+
const say = (message: string) => {
|
|
66
|
+
if (!options.quiet) console.log(message);
|
|
67
|
+
};
|
|
54
68
|
ensurePluginsDir();
|
|
55
69
|
const { url, name } = parseGitHubRef(ref);
|
|
56
70
|
const targetDir = join(PLUGINS_DIR, name);
|
|
@@ -59,11 +73,11 @@ export async function installPlugin(ref: string) {
|
|
|
59
73
|
fail(`Plugin "${name}" already exists.`, `Use "gloomberb update ${name}" to refresh it.`);
|
|
60
74
|
}
|
|
61
75
|
|
|
62
|
-
|
|
63
|
-
|
|
76
|
+
say(cliStyles.accent(`Installing ${name}`));
|
|
77
|
+
say(cliStyles.muted(url));
|
|
64
78
|
|
|
65
79
|
try {
|
|
66
|
-
execFileSync("git", ["clone", "--depth", "1", url, targetDir], { stdio
|
|
80
|
+
execFileSync("git", ["clone", "--depth", "1", url, targetDir], { stdio });
|
|
67
81
|
} catch {
|
|
68
82
|
rmSync(targetDir, { recursive: true, force: true });
|
|
69
83
|
fail(`Failed to clone ${url}.`);
|
|
@@ -71,11 +85,24 @@ export async function installPlugin(ref: string) {
|
|
|
71
85
|
|
|
72
86
|
const pkgPath = join(targetDir, "package.json");
|
|
73
87
|
if (existsSync(pkgPath)) {
|
|
74
|
-
|
|
88
|
+
say(cliStyles.muted("Installing plugin dependencies..."));
|
|
75
89
|
try {
|
|
76
|
-
|
|
90
|
+
// --production: plugin repos depend on `gloomberb` as a devDependency so
|
|
91
|
+
// their own CI can typecheck against the real API. At runtime the host is
|
|
92
|
+
// symlinked in instead, and pulling a second full copy here would both
|
|
93
|
+
// waste a lot of disk and risk a duplicate React.
|
|
94
|
+
execFileSync("bun", ["install", "--production"], { cwd: targetDir, stdio });
|
|
77
95
|
} catch {
|
|
78
|
-
console.error(cliStyles.warning("Warning: failed to install plugin dependencies."));
|
|
96
|
+
if (!options.quiet) console.error(cliStyles.warning("Warning: failed to install plugin dependencies."));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// After `bun install`, which prunes links it does not know about.
|
|
101
|
+
const link = linkHostPackages(targetDir);
|
|
102
|
+
if (link.error) {
|
|
103
|
+
if (!options.quiet) {
|
|
104
|
+
console.error(cliStyles.warning(`Warning: could not link the Gloomberb runtime (${link.error}).`));
|
|
105
|
+
console.error(cliStyles.muted("The plugin's \"gloomberb/*\" imports will not resolve."));
|
|
79
106
|
}
|
|
80
107
|
}
|
|
81
108
|
|
|
@@ -98,13 +125,13 @@ export async function installPlugin(ref: string) {
|
|
|
98
125
|
const mod = await import(entryFile);
|
|
99
126
|
const plugin = mod.default ?? mod.plugin;
|
|
100
127
|
if (plugin?.id && plugin?.name) {
|
|
101
|
-
|
|
128
|
+
say(cliStyles.success(`Installed ${plugin.name} v${plugin.version || "0.0.0"}`));
|
|
102
129
|
return;
|
|
103
130
|
}
|
|
104
131
|
}
|
|
105
|
-
|
|
132
|
+
say(cliStyles.warning("Installed files, but no valid GloomPlugin export was found."));
|
|
106
133
|
} catch (err) {
|
|
107
|
-
|
|
134
|
+
say(cliStyles.warning(`Plugin validation failed: ${err}`));
|
|
108
135
|
}
|
|
109
136
|
}
|
|
110
137
|
|
|
@@ -141,8 +168,9 @@ export async function updatePlugins(name?: string) {
|
|
|
141
168
|
execFileSync("git", ["pull", "--ff-only"], { cwd: targetDir, stdio: "inherit" });
|
|
142
169
|
const pkgPath = join(targetDir, "package.json");
|
|
143
170
|
if (existsSync(pkgPath)) {
|
|
144
|
-
execFileSync("bun", ["install"], { cwd: targetDir, stdio: "inherit" });
|
|
171
|
+
execFileSync("bun", ["install", "--production"], { cwd: targetDir, stdio: "inherit" });
|
|
145
172
|
}
|
|
173
|
+
linkHostPackages(targetDir);
|
|
146
174
|
} catch {
|
|
147
175
|
console.error(cliStyles.danger(`Failed to update ${dir}.`));
|
|
148
176
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { mkdir, mkdtemp, rm, writeFile } from "fs/promises";
|
|
2
2
|
import { tmpdir } from "os";
|
|
3
|
-
import { join } from "path";
|
|
4
|
-
import { pathToFileURL } from "url";
|
|
3
|
+
import { join, resolve, sep } from "path";
|
|
5
4
|
import type { AppConfig } from "../types/config";
|
|
6
5
|
import type { ResolvedSeries } from "../time-series/types";
|
|
7
6
|
import type { OptionsChain, TickerFinancials } from "../types/financials";
|
|
@@ -9,6 +8,7 @@ import type { TickerRecord } from "../types/ticker";
|
|
|
9
8
|
import type { PaneRuntimeState } from "../core/state/app/state";
|
|
10
9
|
import type { RemoteUiNodeSnapshot } from "../remote/types";
|
|
11
10
|
import type { FredSeriesCacheEntry } from "../data/fred-series";
|
|
11
|
+
import type { DatedObservation } from "../plugins/builtin/market-valuation/series";
|
|
12
12
|
import {
|
|
13
13
|
electrobunViewPath,
|
|
14
14
|
writeElectrobunViewPage,
|
|
@@ -21,16 +21,46 @@ export interface DesktopPaneShotPayload {
|
|
|
21
21
|
heightCells: number;
|
|
22
22
|
widthPx: number;
|
|
23
23
|
heightPx: number;
|
|
24
|
+
/** Chrome device scale factor; 2 keeps text crisp, higher values zoom the layout. */
|
|
25
|
+
deviceScaleFactor?: number;
|
|
26
|
+
/** Label drawn at the right edge of the pane title bar; null draws nothing. */
|
|
27
|
+
watermark?: string | null;
|
|
24
28
|
tickers: TickerRecord[];
|
|
25
29
|
financials: Array<[string, TickerFinancials]>;
|
|
26
30
|
optionsChains: Array<[string, OptionsChain]>;
|
|
27
31
|
fredSeries: Array<[string, FredSeriesCacheEntry]>;
|
|
32
|
+
valuationSeries: Array<[string, DatedObservation[]]>;
|
|
33
|
+
statSeries: Array<[string, DatedObservation[]]>;
|
|
28
34
|
capabilitySeries: Array<[string, ResolvedSeries]>;
|
|
29
35
|
paneState: Record<string, PaneRuntimeState>;
|
|
30
36
|
}
|
|
31
37
|
|
|
38
|
+
/** Kept in Bun memory and never serialized into the browser page or CLI result. */
|
|
39
|
+
export interface DesktopPaneShotApiProxy {
|
|
40
|
+
baseUrl: string;
|
|
41
|
+
sessionToken: string | null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface DesktopPaneShotRenderedCell {
|
|
45
|
+
columnId?: string;
|
|
46
|
+
columnLabel: string;
|
|
47
|
+
text: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DesktopPaneShotRenderedRow {
|
|
51
|
+
tableIndex: number;
|
|
52
|
+
rowIndex: number;
|
|
53
|
+
key?: string;
|
|
54
|
+
selected: boolean;
|
|
55
|
+
cells: DesktopPaneShotRenderedCell[];
|
|
56
|
+
}
|
|
57
|
+
|
|
32
58
|
export interface DesktopPaneShotRenderResult {
|
|
33
59
|
visibleText: string;
|
|
60
|
+
rows: DesktopPaneShotRenderedRow[];
|
|
61
|
+
loadingStateDetected: boolean;
|
|
62
|
+
errorStateDetected: boolean;
|
|
63
|
+
errorStateMarkers: string[];
|
|
34
64
|
emptyStateDetected: boolean;
|
|
35
65
|
emptyStateMarkers: string[];
|
|
36
66
|
semanticUi: RemoteUiNodeSnapshot[];
|
|
@@ -47,30 +77,47 @@ type PendingCdpCall = {
|
|
|
47
77
|
reject: (error: Error) => void;
|
|
48
78
|
};
|
|
49
79
|
|
|
80
|
+
const SHOT_MODE_CSS = [
|
|
81
|
+
"[data-gloom-role='composite-chart-toolbar']",
|
|
82
|
+
"[data-gloom-role='chart-series-quick-add']",
|
|
83
|
+
"[data-gloom-role='pane-close']",
|
|
84
|
+
].join(", ") + " { display: none !important; }\n"
|
|
85
|
+
// Sits where the hidden close button was: one cell high, right-aligned in the title bar.
|
|
86
|
+
+ "[data-gloom-role='shot-watermark'] { position: fixed; top: 1px; right: 10px; height: var(--cell-h);"
|
|
87
|
+
+ " line-height: var(--cell-h); font-size: 12px; letter-spacing: 0.02em; color: var(--gloom-text-dim, #888);"
|
|
88
|
+
+ " pointer-events: none; z-index: 1000; }";
|
|
89
|
+
|
|
50
90
|
const CHROME_POLL_ATTEMPTS = 80;
|
|
51
|
-
const SHOT_READY_TIMEOUT_MS =
|
|
91
|
+
const SHOT_READY_TIMEOUT_MS = 45_000;
|
|
52
92
|
const CDP_CALL_TIMEOUT_MS = 10_000;
|
|
53
|
-
const
|
|
93
|
+
const DEFAULT_DEVICE_SCALE_FACTOR = 2;
|
|
94
|
+
const SHOT_API_PROXY_PREFIX = "/__gloom_cli_api__";
|
|
95
|
+
const SESSION_COOKIE_NAMES = ["__Secure-gloomberb.session_token", "gloomberb.session_token"] as const;
|
|
54
96
|
|
|
55
97
|
export async function renderDesktopPaneScreenshot(
|
|
56
98
|
payload: DesktopPaneShotPayload,
|
|
57
99
|
outputPath: string,
|
|
100
|
+
apiProxy: DesktopPaneShotApiProxy,
|
|
58
101
|
): Promise<DesktopPaneShotRenderResult> {
|
|
59
102
|
const tempDir = await mkdtemp(join(tmpdir(), "gloom-pane-shot-"));
|
|
103
|
+
let server: ReturnType<typeof Bun.serve> | null = null;
|
|
60
104
|
try {
|
|
61
105
|
const outdir = join(tempDir, "assets");
|
|
62
106
|
await mkdir(outdir, { recursive: true });
|
|
63
|
-
|
|
107
|
+
await buildShotPage(outdir, payload);
|
|
108
|
+
server = serveShotPage(outdir, apiProxy);
|
|
64
109
|
const chrome = await findChromeExecutable();
|
|
65
110
|
return await capturePageScreenshot({
|
|
66
111
|
chrome,
|
|
67
|
-
url:
|
|
112
|
+
url: new URL("/", server.url).href,
|
|
68
113
|
outputPath,
|
|
69
114
|
widthPx: payload.widthPx,
|
|
70
115
|
heightPx: payload.heightPx,
|
|
116
|
+
deviceScaleFactor: payload.deviceScaleFactor ?? DEFAULT_DEVICE_SCALE_FACTOR,
|
|
71
117
|
userDataDir: join(tempDir, "chrome-profile"),
|
|
72
118
|
});
|
|
73
119
|
} finally {
|
|
120
|
+
server?.stop(true);
|
|
74
121
|
await rm(tempDir, { recursive: true, force: true });
|
|
75
122
|
}
|
|
76
123
|
}
|
|
@@ -90,6 +137,20 @@ async function buildShotPage(outdir: string, payload: DesktopPaneShotPayload): P
|
|
|
90
137
|
loadingText: "Rendering pane...",
|
|
91
138
|
bootstrapScript: `
|
|
92
139
|
window.__GLOOM_CLI_SHOT_PAYLOAD__ = ${payloadJson};
|
|
140
|
+
(() => {
|
|
141
|
+
// A screenshot cannot be interacted with, so drawing tools, the
|
|
142
|
+
// quick-add input and the close button only add noise to the image.
|
|
143
|
+
const style = document.createElement("style");
|
|
144
|
+
style.textContent = ${JSON.stringify(SHOT_MODE_CSS)};
|
|
145
|
+
document.head.appendChild(style);
|
|
146
|
+
const watermark = ${JSON.stringify(payload.watermark ?? null)};
|
|
147
|
+
if (watermark) {
|
|
148
|
+
const mark = document.createElement("div");
|
|
149
|
+
mark.setAttribute("data-gloom-role", "shot-watermark");
|
|
150
|
+
mark.textContent = watermark;
|
|
151
|
+
document.addEventListener("DOMContentLoaded", () => document.body.appendChild(mark));
|
|
152
|
+
}
|
|
153
|
+
})();
|
|
93
154
|
window.addEventListener("error", (event) => {
|
|
94
155
|
window.__GLOOM_CLI_SHOT_ERROR__ = event.error && event.error.stack ? event.error.stack : String(event.error || event.message);
|
|
95
156
|
});
|
|
@@ -100,6 +161,116 @@ async function buildShotPage(outdir: string, payload: DesktopPaneShotPayload): P
|
|
|
100
161
|
});
|
|
101
162
|
}
|
|
102
163
|
|
|
164
|
+
function serveShotPage(
|
|
165
|
+
outdir: string,
|
|
166
|
+
apiProxy: DesktopPaneShotApiProxy,
|
|
167
|
+
): ReturnType<typeof Bun.serve> {
|
|
168
|
+
const staticRoot = resolve(outdir);
|
|
169
|
+
return Bun.serve({
|
|
170
|
+
hostname: "127.0.0.1",
|
|
171
|
+
port: 0,
|
|
172
|
+
idleTimeout: 60,
|
|
173
|
+
fetch(request) {
|
|
174
|
+
const url = new URL(request.url);
|
|
175
|
+
if (url.pathname.startsWith(`${SHOT_API_PROXY_PREFIX}/`)) {
|
|
176
|
+
return proxyShotApiRequest(request, url, apiProxy);
|
|
177
|
+
}
|
|
178
|
+
return serveShotAsset(url, staticRoot);
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async function serveShotAsset(url: URL, staticRoot: string): Promise<Response> {
|
|
184
|
+
let pathname: string;
|
|
185
|
+
try {
|
|
186
|
+
pathname = decodeURIComponent(url.pathname);
|
|
187
|
+
} catch {
|
|
188
|
+
return new Response("Bad request", { status: 400 });
|
|
189
|
+
}
|
|
190
|
+
const relativePath = pathname === "/" ? "index.html" : pathname.replace(/^\/+/, "");
|
|
191
|
+
const filePath = resolve(staticRoot, relativePath);
|
|
192
|
+
if (filePath !== staticRoot && !filePath.startsWith(`${staticRoot}${sep}`)) {
|
|
193
|
+
return new Response("Not found", { status: 404 });
|
|
194
|
+
}
|
|
195
|
+
const file = Bun.file(filePath);
|
|
196
|
+
if (!(await file.exists())) return new Response("Not found", { status: 404 });
|
|
197
|
+
return new Response(file);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function proxyShotApiRequest(
|
|
201
|
+
request: Request,
|
|
202
|
+
requestUrl: URL,
|
|
203
|
+
apiProxy: DesktopPaneShotApiProxy,
|
|
204
|
+
): Promise<Response> {
|
|
205
|
+
const baseUrl = new URL(apiProxy.baseUrl);
|
|
206
|
+
const proxiedPath = requestUrl.pathname.slice(SHOT_API_PROXY_PREFIX.length) || "/";
|
|
207
|
+
const target = new URL(baseUrl);
|
|
208
|
+
const basePath = baseUrl.pathname.replace(/\/+$/, "");
|
|
209
|
+
target.pathname = `${basePath}${proxiedPath.startsWith("/") ? proxiedPath : `/${proxiedPath}`}`;
|
|
210
|
+
target.search = requestUrl.search;
|
|
211
|
+
target.hash = "";
|
|
212
|
+
const headers = new Headers(request.headers);
|
|
213
|
+
for (const name of [
|
|
214
|
+
"authorization",
|
|
215
|
+
"connection",
|
|
216
|
+
"content-length",
|
|
217
|
+
"cookie",
|
|
218
|
+
"host",
|
|
219
|
+
"origin",
|
|
220
|
+
"proxy-authorization",
|
|
221
|
+
"referer",
|
|
222
|
+
"te",
|
|
223
|
+
"trailer",
|
|
224
|
+
"transfer-encoding",
|
|
225
|
+
"upgrade",
|
|
226
|
+
"x-forwarded-for",
|
|
227
|
+
"x-forwarded-host",
|
|
228
|
+
"x-forwarded-proto",
|
|
229
|
+
]) {
|
|
230
|
+
headers.delete(name);
|
|
231
|
+
}
|
|
232
|
+
const browserOwnedHeaders: string[] = [];
|
|
233
|
+
headers.forEach((_value, name) => {
|
|
234
|
+
if (name.startsWith("sec-") || name === "accept-encoding") browserOwnedHeaders.push(name);
|
|
235
|
+
});
|
|
236
|
+
for (const name of browserOwnedHeaders) headers.delete(name);
|
|
237
|
+
headers.set("Origin", baseUrl.origin);
|
|
238
|
+
if (apiProxy.sessionToken) {
|
|
239
|
+
headers.set(
|
|
240
|
+
"Cookie",
|
|
241
|
+
SESSION_COOKIE_NAMES.map((name) => `${name}=${apiProxy.sessionToken}`).join("; "),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
const response = await fetch(target, {
|
|
247
|
+
method: request.method,
|
|
248
|
+
headers,
|
|
249
|
+
body: request.method === "GET" || request.method === "HEAD" ? undefined : request.body,
|
|
250
|
+
redirect: "follow",
|
|
251
|
+
});
|
|
252
|
+
const responseHeaders = new Headers(response.headers);
|
|
253
|
+
for (const name of [
|
|
254
|
+
"access-control-allow-credentials",
|
|
255
|
+
"access-control-allow-origin",
|
|
256
|
+
"content-encoding",
|
|
257
|
+
"content-length",
|
|
258
|
+
"set-cookie",
|
|
259
|
+
"transfer-encoding",
|
|
260
|
+
]) {
|
|
261
|
+
responseHeaders.delete(name);
|
|
262
|
+
}
|
|
263
|
+
responseHeaders.set("Cache-Control", "no-store");
|
|
264
|
+
return new Response(response.body, {
|
|
265
|
+
status: response.status,
|
|
266
|
+
statusText: response.statusText,
|
|
267
|
+
headers: responseHeaders,
|
|
268
|
+
});
|
|
269
|
+
} catch {
|
|
270
|
+
return Response.json({ error: "Cloud API request failed." }, { status: 502 });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
103
274
|
async function findChromeExecutable(): Promise<string> {
|
|
104
275
|
const candidates = [
|
|
105
276
|
process.env.CHROME_PATH,
|
|
@@ -123,6 +294,7 @@ async function capturePageScreenshot({
|
|
|
123
294
|
outputPath,
|
|
124
295
|
widthPx,
|
|
125
296
|
heightPx,
|
|
297
|
+
deviceScaleFactor,
|
|
126
298
|
userDataDir,
|
|
127
299
|
}: {
|
|
128
300
|
chrome: string;
|
|
@@ -130,6 +302,7 @@ async function capturePageScreenshot({
|
|
|
130
302
|
outputPath: string;
|
|
131
303
|
widthPx: number;
|
|
132
304
|
heightPx: number;
|
|
305
|
+
deviceScaleFactor: number;
|
|
133
306
|
userDataDir: string;
|
|
134
307
|
}): Promise<DesktopPaneShotRenderResult> {
|
|
135
308
|
await mkdir(userDataDir, { recursive: true });
|
|
@@ -166,7 +339,7 @@ async function capturePageScreenshot({
|
|
|
166
339
|
await session.send("Emulation.setDeviceMetricsOverride", {
|
|
167
340
|
width: widthPx,
|
|
168
341
|
height: heightPx,
|
|
169
|
-
deviceScaleFactor
|
|
342
|
+
deviceScaleFactor,
|
|
170
343
|
mobile: false,
|
|
171
344
|
});
|
|
172
345
|
await waitForShotReady(session);
|
|
@@ -189,6 +362,21 @@ async function capturePageScreenshot({
|
|
|
189
362
|
}
|
|
190
363
|
}
|
|
191
364
|
|
|
365
|
+
const LOADING_STATE_PATTERNS = [
|
|
366
|
+
/\bLoading(?: [^.]{0,80})?\.{3}/gi,
|
|
367
|
+
/\bRendering pane\.{3}/gi,
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
const ERROR_STATE_PATTERNS = [
|
|
371
|
+
/\b[^.]{1,100} unavailable\./gi,
|
|
372
|
+
/\bFailed to fetch\b/gi,
|
|
373
|
+
/\bCould not load\b/gi,
|
|
374
|
+
/\bSign in to\b/gi,
|
|
375
|
+
/\bVerify your email\b/gi,
|
|
376
|
+
/\bpart of Gloom Cloud Pro\b/gi,
|
|
377
|
+
/\bCloud API request failed\b/gi,
|
|
378
|
+
];
|
|
379
|
+
|
|
192
380
|
const EMPTY_STATE_PATTERNS = [
|
|
193
381
|
/\bNo chart data\b/gi,
|
|
194
382
|
/\bNo graph data\b/gi,
|
|
@@ -197,21 +385,94 @@ const EMPTY_STATE_PATTERNS = [
|
|
|
197
385
|
/\bNo comparison tickers configured\b/gi,
|
|
198
386
|
/\bNo relationship tickers configured\b/gi,
|
|
199
387
|
/\bNo overlapping price history\b/gi,
|
|
200
|
-
/\
|
|
201
|
-
/\bRendering pane\.{0,3}\b/gi,
|
|
202
|
-
/\bNo tickers selected\b/gi,
|
|
203
|
-
/\bMarket data unavailable\b/gi,
|
|
388
|
+
/\bNo tickers? selected\b/gi,
|
|
204
389
|
/\bNo data(?: available)?\b/gi,
|
|
390
|
+
/\bNothing to show yet\b/gi,
|
|
391
|
+
/\bNo transcribed calls(?: for [^.]+)? yet\b/gi,
|
|
392
|
+
/\bNo short interest (?:data|found)\b/gi,
|
|
393
|
+
/\bNo (?:House PTR |insider )?(?:trades|members|transactions|filings)\b/gi,
|
|
205
394
|
];
|
|
206
395
|
|
|
396
|
+
function stateMarkers(text: string, patterns: RegExp[]): string[] {
|
|
397
|
+
return [...new Set(patterns.flatMap((pattern) => (
|
|
398
|
+
[...text.matchAll(pattern)].map((match) => match[0])
|
|
399
|
+
)))];
|
|
400
|
+
}
|
|
401
|
+
|
|
207
402
|
async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneShotRenderResult> {
|
|
208
403
|
const result = await session.send("Runtime.evaluate", {
|
|
209
404
|
expression: `(() => {
|
|
210
405
|
const root = document.getElementById("root") || document.body;
|
|
406
|
+
const semanticUi = window.__GLOOM_CLI_SHOT_SEMANTIC_UI__ || [];
|
|
407
|
+
const normalize = (value) => String(value || "").replace(/\\s+/g, " ").trim();
|
|
408
|
+
const isVisible = (element) => {
|
|
409
|
+
if (!(element instanceof HTMLElement)) return false;
|
|
410
|
+
const style = getComputedStyle(element);
|
|
411
|
+
if (style.display === "none" || style.visibility === "hidden" || style.opacity === "0") return false;
|
|
412
|
+
const rect = element.getBoundingClientRect();
|
|
413
|
+
return rect.width > 0 && rect.height > 0
|
|
414
|
+
&& rect.right > 0 && rect.bottom > 0
|
|
415
|
+
&& rect.left < window.innerWidth && rect.top < window.innerHeight;
|
|
416
|
+
};
|
|
417
|
+
const semanticTables = semanticUi.filter((node) => node && node.role === "table");
|
|
418
|
+
const rows = [];
|
|
419
|
+
[...root.querySelectorAll('[data-gloom-role="data-table"]')]
|
|
420
|
+
.filter(isVisible)
|
|
421
|
+
.forEach((table, tableIndex) => {
|
|
422
|
+
const tableMetadata = semanticTables[tableIndex] && semanticTables[tableIndex].metadata || {};
|
|
423
|
+
const semanticColumns = Array.isArray(tableMetadata.columns) ? tableMetadata.columns : [];
|
|
424
|
+
const semanticRows = Array.isArray(tableMetadata.rows) ? tableMetadata.rows : [];
|
|
425
|
+
const headers = [...table.querySelectorAll('[data-gloom-role="data-table-header-cell"]')]
|
|
426
|
+
.map((cell) => normalize(cell.innerText || cell.textContent));
|
|
427
|
+
const rowElements = [...table.querySelectorAll(
|
|
428
|
+
'[data-gloom-role="data-table-row"], [data-gloom-role="data-table-section-header"]',
|
|
429
|
+
)].filter(isVisible);
|
|
430
|
+
rowElements.forEach((row, rowIndex) => {
|
|
431
|
+
const cellElements = [...row.querySelectorAll('[data-gloom-role="data-table-cell"]')];
|
|
432
|
+
const values = cellElements.length > 0 ? cellElements : [row];
|
|
433
|
+
const semanticRow = semanticRows[rowIndex] || {};
|
|
434
|
+
const cells = values.map((cell, cellIndex) => {
|
|
435
|
+
const semanticColumn = semanticColumns[cellIndex] || {};
|
|
436
|
+
return {
|
|
437
|
+
...(typeof semanticColumn.id === "string" ? { columnId: semanticColumn.id } : {}),
|
|
438
|
+
columnLabel: typeof semanticColumn.label === "string"
|
|
439
|
+
? semanticColumn.label
|
|
440
|
+
: headers[cellIndex] || (values.length === 1 ? "Row" : String(cellIndex + 1)),
|
|
441
|
+
text: normalize(cell.innerText || cell.textContent),
|
|
442
|
+
};
|
|
443
|
+
}).filter((cell) => cell.text.length > 0);
|
|
444
|
+
if (cells.length === 0) return;
|
|
445
|
+
rows.push({
|
|
446
|
+
tableIndex,
|
|
447
|
+
rowIndex,
|
|
448
|
+
...(typeof semanticRow.key === "string" ? { key: semanticRow.key } : {}),
|
|
449
|
+
selected: row.getAttribute("data-selected") === "true" || semanticRow.selected === true,
|
|
450
|
+
cells,
|
|
451
|
+
});
|
|
452
|
+
});
|
|
453
|
+
});
|
|
454
|
+
if (rows.length === 0) {
|
|
455
|
+
[...root.querySelectorAll('[data-gloom-role="desktop-list-row"]')]
|
|
456
|
+
.filter(isVisible)
|
|
457
|
+
.forEach((row, rowIndex) => {
|
|
458
|
+
const text = normalize(row.innerText || row.textContent);
|
|
459
|
+
if (!text) return;
|
|
460
|
+
rows.push({
|
|
461
|
+
tableIndex: 0,
|
|
462
|
+
rowIndex,
|
|
463
|
+
selected: row.getAttribute("data-selected") === "true",
|
|
464
|
+
cells: [{ columnLabel: "Row", text }],
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
}
|
|
211
468
|
return {
|
|
212
469
|
visibleText: root.innerText || root.textContent || "",
|
|
213
470
|
error: window.__GLOOM_CLI_SHOT_ERROR__ || "",
|
|
214
|
-
|
|
471
|
+
loadingStateDetected: root.querySelector('[data-gloom-status="loading"]') !== null,
|
|
472
|
+
errorStateDetected: root.querySelector('[data-gloom-status="error"]') !== null,
|
|
473
|
+
emptyStateDetected: root.querySelector('[data-gloom-status="empty"]') !== null,
|
|
474
|
+
rows,
|
|
475
|
+
semanticUi,
|
|
215
476
|
};
|
|
216
477
|
})()`,
|
|
217
478
|
returnByValue: true,
|
|
@@ -220,6 +481,10 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
|
|
|
220
481
|
value?: {
|
|
221
482
|
visibleText?: string;
|
|
222
483
|
error?: string;
|
|
484
|
+
loadingStateDetected?: boolean;
|
|
485
|
+
errorStateDetected?: boolean;
|
|
486
|
+
emptyStateDetected?: boolean;
|
|
487
|
+
rows?: DesktopPaneShotRenderedRow[];
|
|
223
488
|
semanticUi?: RemoteUiNodeSnapshot[];
|
|
224
489
|
};
|
|
225
490
|
};
|
|
@@ -227,12 +492,16 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
|
|
|
227
492
|
const value = result.result?.value;
|
|
228
493
|
if (value?.error) throw new Error(value.error);
|
|
229
494
|
const visibleText = (value?.visibleText ?? "").replace(/\s+/g, " ").trim();
|
|
230
|
-
const
|
|
231
|
-
|
|
232
|
-
)
|
|
495
|
+
const loadingStateMarkers = stateMarkers(visibleText, LOADING_STATE_PATTERNS);
|
|
496
|
+
const errorStateMarkers = stateMarkers(visibleText, ERROR_STATE_PATTERNS);
|
|
497
|
+
const emptyStateMarkers = stateMarkers(visibleText, EMPTY_STATE_PATTERNS);
|
|
233
498
|
return {
|
|
234
499
|
visibleText,
|
|
235
|
-
|
|
500
|
+
rows: Array.isArray(value?.rows) ? value.rows : [],
|
|
501
|
+
loadingStateDetected: value?.loadingStateDetected === true || loadingStateMarkers.length > 0,
|
|
502
|
+
errorStateDetected: value?.errorStateDetected === true || errorStateMarkers.length > 0,
|
|
503
|
+
errorStateMarkers,
|
|
504
|
+
emptyStateDetected: value?.emptyStateDetected === true || emptyStateMarkers.length > 0,
|
|
236
505
|
emptyStateMarkers,
|
|
237
506
|
semanticUi: Array.isArray(value?.semanticUi) ? value.semanticUi : [],
|
|
238
507
|
};
|
package/src/cli/index.ts
CHANGED
|
@@ -121,7 +121,7 @@ function createCoreCliCommands(
|
|
|
121
121
|
aliases: ["screenshot"],
|
|
122
122
|
description: "Render a desktop-style screenshot for a pane-backed market function",
|
|
123
123
|
help: {
|
|
124
|
-
usage: ["shot <function-or-pane> [argument] [--output path] [--width px] [--height px] [--key value]"],
|
|
124
|
+
usage: ["shot <function-or-pane> [argument] [--output path] [--width px] [--height px] [--theme id] [--scale n] [--watermark text] [--key value]"],
|
|
125
125
|
},
|
|
126
126
|
execute: async (args, ctx) => {
|
|
127
127
|
await runPaneScreenshot(args, ctx);
|