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
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} 登入",
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { Box,
|
|
3
|
-
import {
|
|
2
|
+
import { Box, Text, TextAttributes, type InputRenderable } from "../ui";
|
|
3
|
+
import { InputSearchBar, usePaneFooter } from "../components";
|
|
4
4
|
import { ListView, type ListViewItem } from "../components/ui/list-view";
|
|
5
5
|
import { useShortcut, useViewport } from "../react/input";
|
|
6
|
-
import { useAppInputCapture } from "../state/app/input-capture";
|
|
7
6
|
import { useThemeColors } from "../theme/theme-context";
|
|
8
7
|
import { truncateToDisplayWidth } from "../utils/format";
|
|
8
|
+
import { isPlainKey } from "../utils/keyboard";
|
|
9
9
|
import { t, tf } from "../i18n";
|
|
10
10
|
import type { LayoutGalleryController } from "./gallery";
|
|
11
11
|
import {
|
|
@@ -71,9 +71,23 @@ export function LayoutGalleryTerminal({
|
|
|
71
71
|
const detailsWidth = Math.min(DETAILS_WIDTH, Math.max(28, Math.floor(paneWidth * 0.42)));
|
|
72
72
|
const inputRef = useRef<InputRenderable | null>(null);
|
|
73
73
|
const [searchFocused, setSearchFocused] = useState(false);
|
|
74
|
+
const [searchFocusToken, setSearchFocusToken] = useState(0);
|
|
74
75
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
const {
|
|
77
|
+
activate: activateLayout,
|
|
78
|
+
canDelete,
|
|
79
|
+
community,
|
|
80
|
+
copyLink,
|
|
81
|
+
deleteLayout,
|
|
82
|
+
duplicateLayout,
|
|
83
|
+
install: installLayout,
|
|
84
|
+
newLayout,
|
|
85
|
+
owned,
|
|
86
|
+
publishCurrent,
|
|
87
|
+
publishing,
|
|
88
|
+
renameLayout,
|
|
89
|
+
select,
|
|
90
|
+
} = controller;
|
|
77
91
|
const discoverStatus = discoverStatusRow(controller);
|
|
78
92
|
|
|
79
93
|
const rows = useMemo<GalleryRow[]>(() => {
|
|
@@ -138,6 +152,12 @@ export function LayoutGalleryTerminal({
|
|
|
138
152
|
setSelectedIndex(selectableIndexes[nextPosition]!);
|
|
139
153
|
}, [selectableIndexes, selectedIndex]);
|
|
140
154
|
|
|
155
|
+
const focusSearch = useCallback(() => {
|
|
156
|
+
setSearchFocused(true);
|
|
157
|
+
setSearchFocusToken((current) => current + 1);
|
|
158
|
+
}, []);
|
|
159
|
+
const blurSearch = useCallback(() => setSearchFocused(false), []);
|
|
160
|
+
|
|
141
161
|
// The details panel is always on screen here, so Enter runs the action it shows
|
|
142
162
|
// instead of stepping through a separate detail state.
|
|
143
163
|
const activate = useCallback((row: GalleryRow | null) => {
|
|
@@ -147,70 +167,120 @@ export function LayoutGalleryTerminal({
|
|
|
147
167
|
return;
|
|
148
168
|
}
|
|
149
169
|
if (!row.entry) return;
|
|
150
|
-
if (row.entry.kind === "community")
|
|
151
|
-
else
|
|
152
|
-
}, [
|
|
170
|
+
if (row.entry.kind === "community") installLayout(row.entry);
|
|
171
|
+
else activateLayout(row.entry);
|
|
172
|
+
}, [activateLayout, installLayout]);
|
|
173
|
+
|
|
174
|
+
const selectedRowRef = useRef<GalleryRow | null>(selectedRow);
|
|
175
|
+
selectedRowRef.current = selectedRow;
|
|
176
|
+
const activateSelected = useCallback(() => activate(selectedRowRef.current), [activate]);
|
|
177
|
+
const renameSelected = useCallback(() => {
|
|
178
|
+
const entry = selectedRowRef.current?.entry;
|
|
179
|
+
if (entry?.kind === "owned") renameLayout(entry);
|
|
180
|
+
}, [renameLayout]);
|
|
181
|
+
const copySelected = useCallback(() => {
|
|
182
|
+
const entry = selectedRowRef.current?.entry;
|
|
183
|
+
if (entry?.kind === "community") copyLink(entry);
|
|
184
|
+
else if (entry?.kind === "owned") duplicateLayout(entry);
|
|
185
|
+
}, [copyLink, duplicateLayout]);
|
|
186
|
+
const deleteSelected = useCallback(() => {
|
|
187
|
+
const entry = selectedRowRef.current?.entry;
|
|
188
|
+
if (entry?.kind === "owned") deleteLayout(entry);
|
|
189
|
+
}, [deleteLayout]);
|
|
190
|
+
|
|
191
|
+
usePaneFooter("layout-marketplace", () => ({
|
|
192
|
+
info: publishing
|
|
193
|
+
? [{ id: "publishing", parts: [{ text: "publishing", tone: "muted" as const }] }]
|
|
194
|
+
: [],
|
|
195
|
+
hints: [
|
|
196
|
+
{ id: "search", key: "/", label: "search", onPress: focusSearch },
|
|
197
|
+
{ id: "new", key: "n", label: "ew", onPress: newLayout },
|
|
198
|
+
...(selectedEntry?.kind === "owned"
|
|
199
|
+
? [
|
|
200
|
+
{ id: "open", key: "o", label: "pen", onPress: activateSelected },
|
|
201
|
+
{ id: "rename", key: "r", label: "ename", onPress: renameSelected },
|
|
202
|
+
{ id: "copy", key: "c", label: "opy", onPress: copySelected },
|
|
203
|
+
{ id: "delete", key: "d", label: "elete", onPress: deleteSelected, disabled: !canDelete },
|
|
204
|
+
]
|
|
205
|
+
: selectedEntry?.kind === "community"
|
|
206
|
+
? [
|
|
207
|
+
{ id: "add", key: "a", label: "dd layout", onPress: activateSelected },
|
|
208
|
+
{ id: "copy-link", key: "c", label: "opy link", onPress: copySelected },
|
|
209
|
+
]
|
|
210
|
+
: selectedRow?.id === "discover:retry"
|
|
211
|
+
? [{ id: "retry", key: "r", label: "etry", onPress: activateSelected }]
|
|
212
|
+
: selectedRow?.action
|
|
213
|
+
? [{ id: "open", key: "o", label: "pen", onPress: activateSelected }]
|
|
214
|
+
: []),
|
|
215
|
+
{ id: "publish", key: "p", label: "ublish", onPress: publishCurrent, disabled: publishing },
|
|
216
|
+
],
|
|
217
|
+
}), [
|
|
218
|
+
activateSelected,
|
|
219
|
+
canDelete,
|
|
220
|
+
copySelected,
|
|
221
|
+
deleteSelected,
|
|
222
|
+
focusSearch,
|
|
223
|
+
newLayout,
|
|
224
|
+
publishCurrent,
|
|
225
|
+
publishing,
|
|
226
|
+
renameSelected,
|
|
227
|
+
selectedEntry?.kind,
|
|
228
|
+
selectedRow?.action,
|
|
229
|
+
selectedRow?.id,
|
|
230
|
+
]);
|
|
153
231
|
|
|
154
232
|
useShortcut((event) => {
|
|
155
233
|
if (dialogOpen) return;
|
|
156
|
-
const editable = event.targetEditable === true;
|
|
157
234
|
// Leaving the search field is the first Escape; the gallery closes on the next.
|
|
158
|
-
if (event
|
|
159
|
-
event.preventDefault();
|
|
160
|
-
event.stopPropagation();
|
|
161
|
-
setSearchFocused(false);
|
|
162
|
-
return;
|
|
163
|
-
}
|
|
164
|
-
if (event.name === "down" || (!editable && event.name === "j")) {
|
|
235
|
+
if (isPlainKey(event, "escape", "esc") && searchFocused) {
|
|
165
236
|
event.preventDefault();
|
|
166
237
|
event.stopPropagation();
|
|
167
|
-
|
|
238
|
+
blurSearch();
|
|
168
239
|
return;
|
|
169
240
|
}
|
|
170
|
-
if (event.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
move(-1);
|
|
174
|
-
return;
|
|
175
|
-
}
|
|
176
|
-
if (event.name === "enter" || event.name === "return") {
|
|
177
|
-
event.preventDefault();
|
|
178
|
-
event.stopPropagation();
|
|
179
|
-
activate(selectedRow);
|
|
180
|
-
return;
|
|
181
|
-
}
|
|
182
|
-
if (!editable && (event.name === "/" || event.sequence === "/")) {
|
|
241
|
+
if (event.targetEditable) return;
|
|
242
|
+
|
|
243
|
+
const run = (action: () => void) => {
|
|
183
244
|
event.preventDefault();
|
|
184
245
|
event.stopPropagation();
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
246
|
+
action();
|
|
247
|
+
};
|
|
248
|
+
if (isPlainKey(event, "down", "j")) run(() => move(1));
|
|
249
|
+
else if (isPlainKey(event, "up", "k")) {
|
|
250
|
+
run(() => {
|
|
251
|
+
if (event.name === "up" && selectedIndex === selectableIndexes[0]) focusSearch();
|
|
252
|
+
else move(-1);
|
|
253
|
+
});
|
|
254
|
+
} else if (isPlainKey(event, "enter", "return")) run(activateSelected);
|
|
255
|
+
else if (isPlainKey(event, "/")) run(focusSearch);
|
|
256
|
+
else if (isPlainKey(event, "n")) run(newLayout);
|
|
257
|
+
else if (isPlainKey(event, "p")) run(publishCurrent);
|
|
258
|
+
else if (isPlainKey(event, "o") && (selectedEntry?.kind === "owned" || selectedRow?.action)) run(activateSelected);
|
|
259
|
+
else if (isPlainKey(event, "a") && selectedEntry?.kind === "community") run(activateSelected);
|
|
260
|
+
else if (isPlainKey(event, "r") && selectedRow?.id === "discover:retry") run(activateSelected);
|
|
261
|
+
else if (isPlainKey(event, "r") && selectedEntry?.kind === "owned") run(renameSelected);
|
|
262
|
+
else if (isPlainKey(event, "c") && selectedEntry) run(copySelected);
|
|
263
|
+
else if (isPlainKey(event, "d") && selectedEntry?.kind === "owned" && canDelete) run(deleteSelected);
|
|
188
264
|
}, { allowEditable: true, enabled: focused && !dialogOpen, phase: "before", scope: "layout-gallery" });
|
|
189
265
|
|
|
190
266
|
const bodyHeight = Math.max(4, paneHeight - 1);
|
|
191
267
|
|
|
192
268
|
return (
|
|
193
269
|
<Box flexGrow={1} flexDirection="column" backgroundColor={colors.bg}>
|
|
194
|
-
<
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
onChange={controller.setQuery}
|
|
209
|
-
onFocus={() => setSearchFocused(true)}
|
|
210
|
-
onBlur={() => setSearchFocused(false)}
|
|
211
|
-
onSubmit={() => setSearchFocused(false)}
|
|
212
|
-
/>
|
|
213
|
-
</Box>
|
|
270
|
+
<InputSearchBar
|
|
271
|
+
value={controller.query}
|
|
272
|
+
focused={focused && !dialogOpen}
|
|
273
|
+
active={searchFocused}
|
|
274
|
+
width={paneWidth}
|
|
275
|
+
focusToken={searchFocusToken}
|
|
276
|
+
inputRef={inputRef}
|
|
277
|
+
placeholder={t("Search layouts and panes")}
|
|
278
|
+
debounceMs={80}
|
|
279
|
+
onFocus={focusSearch}
|
|
280
|
+
onBlur={blurSearch}
|
|
281
|
+
onNavigateDown={blurSearch}
|
|
282
|
+
onQueryChange={controller.setQuery}
|
|
283
|
+
/>
|
|
214
284
|
|
|
215
285
|
<Box flexDirection="row" flexGrow={1}>
|
|
216
286
|
<Box flexGrow={1} flexDirection="column" paddingX={1}>
|
|
@@ -257,17 +327,12 @@ export function LayoutGalleryTerminal({
|
|
|
257
327
|
controller={controller}
|
|
258
328
|
entry={selectedEntry}
|
|
259
329
|
width={detailsWidth}
|
|
260
|
-
height={
|
|
330
|
+
height={bodyHeight}
|
|
261
331
|
/>
|
|
262
332
|
) : selectedRow?.action ? (
|
|
263
333
|
<Box flexDirection="column" gap={1}>
|
|
264
334
|
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{selectedRow.label}</Text>
|
|
265
335
|
{selectedRow.detail && <Text fg={colors.textMuted}>{selectedRow.detail}</Text>}
|
|
266
|
-
<Button
|
|
267
|
-
label={selectedRow.id === "discover:login" ? "Log in" : "Retry"}
|
|
268
|
-
variant="primary"
|
|
269
|
-
onPress={selectedRow.action}
|
|
270
|
-
/>
|
|
271
336
|
</Box>
|
|
272
337
|
) : (
|
|
273
338
|
<Text fg={colors.textDim}>{t("No layout selected.")}</Text>
|
|
@@ -292,8 +357,8 @@ function LayoutDetails({
|
|
|
292
357
|
const colors = useThemeColors();
|
|
293
358
|
const allPanes = summarizeLayoutPanes(entry.layout, controller.panes);
|
|
294
359
|
const missing = allPanes.filter((pane) => pane.missing);
|
|
295
|
-
// Leave room for the title block
|
|
296
|
-
const paneBudget = Math.max(3, height -
|
|
360
|
+
// Leave room for the title block and the optional warning.
|
|
361
|
+
const paneBudget = Math.max(3, height - 8);
|
|
297
362
|
const overflow = Math.max(0, allPanes.length - paneBudget);
|
|
298
363
|
const panes = overflow > 0 ? allPanes.slice(0, paneBudget - 1) : allPanes;
|
|
299
364
|
|
|
@@ -336,41 +401,6 @@ function LayoutDetails({
|
|
|
336
401
|
</Text>
|
|
337
402
|
</>
|
|
338
403
|
)}
|
|
339
|
-
<Box height={1} />
|
|
340
|
-
<Box flexDirection="row" gap={1}>
|
|
341
|
-
<Button
|
|
342
|
-
label={entry.kind === "community" ? "Add Layout" : "Open"}
|
|
343
|
-
variant="primary"
|
|
344
|
-
onPress={() => (entry.kind === "community"
|
|
345
|
-
? controller.install(entry)
|
|
346
|
-
: controller.activate(entry))}
|
|
347
|
-
/>
|
|
348
|
-
{entry.kind === "community" && (
|
|
349
|
-
<Button label="Copy Link" variant="secondary" onPress={() => controller.copyLink(entry)} />
|
|
350
|
-
)}
|
|
351
|
-
{entry.kind === "owned" && (
|
|
352
|
-
<>
|
|
353
|
-
<Button label="Rename" variant="secondary" onPress={() => controller.renameLayout(entry)} />
|
|
354
|
-
<Button label="Copy" variant="secondary" onPress={() => controller.duplicateLayout(entry)} />
|
|
355
|
-
<Button
|
|
356
|
-
label="Delete"
|
|
357
|
-
variant="secondary"
|
|
358
|
-
disabled={!controller.canDelete}
|
|
359
|
-
onPress={() => controller.deleteLayout(entry)}
|
|
360
|
-
/>
|
|
361
|
-
</>
|
|
362
|
-
)}
|
|
363
|
-
</Box>
|
|
364
|
-
<Box height={1} />
|
|
365
|
-
<Box flexDirection="row" gap={1}>
|
|
366
|
-
<Button label="New" variant="ghost" onPress={controller.newLayout} />
|
|
367
|
-
<Button
|
|
368
|
-
label={controller.publishing ? "Publishing" : "Publish"}
|
|
369
|
-
variant="ghost"
|
|
370
|
-
disabled={controller.publishing}
|
|
371
|
-
onPress={controller.publishCurrent}
|
|
372
|
-
/>
|
|
373
|
-
</Box>
|
|
374
404
|
</Box>
|
|
375
405
|
);
|
|
376
406
|
}
|
|
@@ -4,6 +4,8 @@ import { testRender } from "../renderers/opentui/test-utils";
|
|
|
4
4
|
import { AppContext, createInitialState, type AppAction } from "../state/app/context";
|
|
5
5
|
import { cloneLayout, createDefaultConfig } from "../types/config";
|
|
6
6
|
import type { PaneDef } from "../types/plugin";
|
|
7
|
+
import { Box } from "../ui";
|
|
8
|
+
import { PaneFooterBar, PaneFooterProvider } from "../components/layout/pane/footer";
|
|
7
9
|
import type { PluginRegistry } from "../plugins/registry";
|
|
8
10
|
import { LayoutMarketplaceGallery } from "./gallery";
|
|
9
11
|
|
|
@@ -43,7 +45,21 @@ async function renderGallery() {
|
|
|
43
45
|
|
|
44
46
|
testSetup = await testRender(
|
|
45
47
|
<AppContext value={{ state, dispatch: (action) => actions.push(action) }}>
|
|
46
|
-
<
|
|
48
|
+
<PaneFooterProvider>
|
|
49
|
+
{(footer) => (
|
|
50
|
+
<Box width={100} height={24} flexDirection="column">
|
|
51
|
+
<Box width={100} height={23}>
|
|
52
|
+
<LayoutMarketplaceGallery
|
|
53
|
+
pluginRegistry={registry}
|
|
54
|
+
width={100}
|
|
55
|
+
height={23}
|
|
56
|
+
onClose={() => { closed = true; }}
|
|
57
|
+
/>
|
|
58
|
+
</Box>
|
|
59
|
+
<PaneFooterBar footer={footer} focused width={100} />
|
|
60
|
+
</Box>
|
|
61
|
+
)}
|
|
62
|
+
</PaneFooterProvider>
|
|
47
63
|
</AppContext>,
|
|
48
64
|
{ width: 100, height: 24 },
|
|
49
65
|
);
|
|
@@ -65,6 +81,54 @@ test("lists owned layouts before Discover and details the selected layout", asyn
|
|
|
65
81
|
expect(frame).toContain("Portfolio");
|
|
66
82
|
expect(frame).toContain("Ticker Research");
|
|
67
83
|
expect(frame).toContain("3 docked");
|
|
84
|
+
|
|
85
|
+
const searchLine = frame.split("\n").find((line) => line.includes("Search layouts and panes"));
|
|
86
|
+
expect(searchLine?.startsWith("/ Search layouts and panes")).toBe(true);
|
|
87
|
+
expect(frame).toContain("[/]search");
|
|
88
|
+
expect(frame).toContain("[n]ew");
|
|
89
|
+
expect(frame).toContain("[o]pen");
|
|
90
|
+
expect(frame).toContain("[r]ename");
|
|
91
|
+
expect(frame).toContain("[c]opy");
|
|
92
|
+
expect(frame).toContain("[d]elete");
|
|
93
|
+
expect(frame).toContain("[p]ublish");
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("search Enter returns to the list before activating the filtered layout", async () => {
|
|
97
|
+
const { actions, isClosed } = await renderGallery();
|
|
98
|
+
|
|
99
|
+
await act(async () => {
|
|
100
|
+
testSetup!.mockInput.pressKey("/");
|
|
101
|
+
await testSetup!.renderOnce();
|
|
102
|
+
});
|
|
103
|
+
await act(async () => {
|
|
104
|
+
await testSetup!.mockInput.typeText("Research");
|
|
105
|
+
testSetup!.mockInput.pressEnter();
|
|
106
|
+
await testSetup!.renderOnce();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(isClosed()).toBe(false);
|
|
110
|
+
expect(actions.some((action) => action.type === "SWITCH_LAYOUT")).toBe(false);
|
|
111
|
+
expect(testSetup!.captureCharFrame()).toContain("/ Research");
|
|
112
|
+
|
|
113
|
+
await act(async () => {
|
|
114
|
+
testSetup!.mockInput.pressEnter();
|
|
115
|
+
await testSetup!.renderOnce();
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(actions).toContainEqual({ type: "SWITCH_LAYOUT", index: 1 });
|
|
119
|
+
expect(isClosed()).toBe(true);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test("n opens the new layout workflow", async () => {
|
|
123
|
+
const { isClosed } = await renderGallery();
|
|
124
|
+
|
|
125
|
+
await act(async () => {
|
|
126
|
+
testSetup!.mockInput.pressKey("n");
|
|
127
|
+
await testSetup!.renderOnce();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(isClosed()).toBe(false);
|
|
131
|
+
expect(testSetup!.captureCharFrame()).toContain("Create Layout");
|
|
68
132
|
});
|
|
69
133
|
|
|
70
134
|
test("j/k move the selection and Enter switches to the layout and closes", async () => {
|
|
@@ -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
|
|