gloomberb 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +38 -15
- package/README.zh-CN.md +12 -12
- package/package.json +10 -3
- package/src/api-client/auth.ts +14 -10
- package/src/api-client/data.ts +54 -0
- package/src/api-client/index.test.ts +124 -0
- package/src/api-client/index.ts +139 -4
- package/src/api-client/paths.ts +89 -0
- package/src/api-client/request.ts +15 -2
- package/src/api-client/socket-health.test.ts +1 -0
- package/src/api-client/socket.ts +2 -1
- package/src/api-client/types.ts +163 -0
- package/src/app/pane-runtime/index.tsx +4 -1
- package/src/app/pane-runtime/plugin-bindings.test.ts +117 -0
- package/src/app/pane-runtime/plugin-bindings.ts +25 -4
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +25 -14
- package/src/app/runtime/desktop-deeplink.test.ts +16 -0
- package/src/app/runtime/desktop-deeplink.ts +66 -2
- package/src/app/runtime/desktop-menu.ts +3 -0
- package/src/app/runtime/plugin-bindings.ts +30 -28
- package/src/app/runtime/update.ts +8 -6
- package/src/app.tsx +21 -7
- package/src/architecture/import-boundaries.test.ts +5 -1
- package/src/capabilities/factories.ts +4 -0
- package/src/cli/commands/automation.test.ts +9 -0
- package/src/cli/commands/automation.ts +22 -73
- package/src/cli/index.ts +10 -6
- package/src/cli/registry.ts +1 -1
- package/src/components/chart/composite/composite-chart.test.tsx +45 -2
- package/src/components/chart/composite/composite-chart.tsx +16 -12
- package/src/components/chart/composite/format.test.ts +34 -0
- package/src/components/chart/composite/format.ts +33 -10
- package/src/components/chart/composite/scene.ts +0 -21
- package/src/components/chart/composite/tools.ts +49 -0
- package/src/components/chart/core/axis-format.ts +13 -3
- package/src/components/chart/core/index.test.ts +7 -0
- package/src/components/chart/core/renderer.ts +3 -4
- package/src/components/command-bar/commands/direct/index.ts +4 -0
- package/src/components/command-bar/commands/registry.ts +8 -1
- package/src/components/command-bar/layout-items/current-layout.ts +1 -1
- package/src/components/command-bar/pane-templates/workflow.ts +4 -0
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -0
- package/src/components/command-bar/routes/root/shortcuts.test.ts +19 -0
- package/src/components/command-bar/routes/root/shortcuts.ts +3 -1
- package/src/components/command-bar/surface/index.test.tsx +6 -6
- package/src/components/command-bar/surface/index.tsx +4 -1
- package/src/components/command-bar/surface/pane.test.tsx +66 -2
- package/src/components/command-bar/view-model.test.ts +2 -1
- package/src/components/command-bar/workflow/ops.test.ts +54 -0
- package/src/components/command-bar/workflow/ops.ts +1 -1
- package/src/components/company-logo.test.ts +19 -0
- package/src/components/company-logo.tsx +60 -0
- package/src/components/feed-data-table/stack-view.tsx +7 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/detached-pane-shell.tsx +85 -15
- package/src/components/layout/header.tsx +5 -5
- package/src/components/layout/pane/title.ts +31 -3
- package/src/components/layout/shell/active-drag.ts +4 -11
- package/src/components/layout/shell/drag/index.ts +4 -20
- package/src/components/layout/shell/drag/runtime.ts +3 -4
- package/src/components/layout/shell/index.test.tsx +17 -3
- package/src/components/layout/shell/index.tsx +89 -31
- package/src/components/layout/shell/menu.test.ts +37 -1
- package/src/components/layout/shell/menu.ts +14 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +22 -8
- package/src/components/layout/shell/pane/actions.ts +0 -5
- package/src/components/layout/shell/pane/management-shortcuts.ts +9 -4
- package/src/components/layout/shell/shortcuts.ts +6 -3
- package/src/components/layout/shell/ticker-link-menu.test.ts +82 -0
- package/src/components/layout/shell/ticker-link-menu.ts +78 -0
- package/src/components/layout/status-bar.test.tsx +62 -61
- package/src/components/layout/status-bar.tsx +53 -75
- package/src/components/onboarding/account-step/form-panel.tsx +4 -2
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.test.tsx +44 -1
- package/src/components/onboarding/onboarding-wizard.tsx +13 -5
- package/src/components/onboarding/wizard-account.ts +4 -23
- package/src/components/table-view-shared.test.ts +11 -0
- package/src/components/table-view-shared.tsx +26 -0
- package/src/components/ui/data-table/index.tsx +19 -1
- package/src/components/ui/fields.tsx +5 -1
- package/src/core/state/app/layout-reducer.ts +36 -0
- package/src/core/state/app/layout.ts +10 -5
- package/src/core/state/app/state.ts +0 -13
- package/src/core/state/app/types.ts +6 -4
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/config/layout.ts +2 -0
- package/src/data/config/store/index.test.ts +27 -6
- package/src/data/config/store/normalize.ts +3 -5
- package/src/data/plugin-state-store.ts +11 -0
- package/src/data/sqlite/cache.test.ts +10 -0
- package/src/i18n/es.ts +13 -5
- package/src/i18n/ja.ts +13 -5
- package/src/i18n/ko.ts +13 -5
- package/src/i18n/zh-cn.ts +13 -5
- package/src/i18n/zh-tw.ts +13 -5
- package/src/layout-marketplace/api.test.ts +54 -0
- package/src/layout-marketplace/api.ts +65 -0
- package/src/layout-marketplace/gallery-desktop.test.tsx +262 -0
- package/src/layout-marketplace/gallery-desktop.tsx +375 -0
- package/src/layout-marketplace/gallery-terminal.tsx +376 -0
- package/src/layout-marketplace/gallery.test.tsx +84 -0
- package/src/layout-marketplace/gallery.tsx +323 -0
- package/src/layout-marketplace/geometry.test.ts +47 -0
- package/src/layout-marketplace/geometry.ts +80 -0
- package/src/layout-marketplace/mini-workspace.test.tsx +122 -0
- package/src/layout-marketplace/mini-workspace.tsx +428 -0
- package/src/layout-marketplace/model.test.ts +118 -0
- package/src/layout-marketplace/model.ts +171 -0
- package/src/layout-marketplace/name-dialog.tsx +60 -0
- package/src/layout-marketplace/pane-imagery.ts +85 -0
- package/src/layout-marketplace/pane.tsx +20 -0
- package/src/layout-marketplace/payload.test.ts +246 -0
- package/src/layout-marketplace/payload.ts +624 -0
- package/src/layout-marketplace/use-marketplace.ts +63 -0
- package/src/market-data/coordinator/index.test.ts +2 -2
- package/src/market-data/coordinator/index.ts +1 -1
- package/src/market-data/hooks.tsx +0 -41
- package/src/market-data/market/format.test.ts +7 -0
- package/src/market-data/market/freshness.ts +5 -2
- package/src/market-data/quotes/freshness.test.ts +16 -0
- package/src/news/aggregator.test.ts +92 -3
- package/src/news/aggregator.ts +98 -18
- package/src/news/hooks.test.tsx +2 -0
- package/src/news/hooks.ts +27 -1
- package/src/news/news-model.ts +5 -2
- package/src/news/types.ts +7 -0
- package/src/plugins/broker-sync/broker-sync.test.ts +58 -0
- package/src/plugins/broker-sync/normalize.ts +42 -5
- package/src/plugins/builtin/account-management/pane.tsx +5 -5
- package/src/plugins/builtin/ai/index.tsx +18 -2
- package/src/plugins/builtin/ai/screener/pane.test.tsx +47 -4
- package/src/plugins/builtin/ai/screener/pane.tsx +35 -12
- package/src/plugins/builtin/alerts/alert-engine.test.ts +53 -1
- package/src/plugins/builtin/alerts/alert-engine.ts +34 -1
- package/src/plugins/builtin/alerts/index.test.tsx +49 -6
- package/src/plugins/builtin/alerts/index.tsx +11 -6
- package/src/plugins/builtin/alerts/pane.tsx +51 -9
- package/src/plugins/builtin/alerts/quotes.ts +11 -3
- package/src/plugins/builtin/alerts/types.ts +1 -0
- package/src/plugins/builtin/analytics/index.tsx +11 -1
- package/src/plugins/builtin/analytics/pane-model.ts +6 -4
- package/src/plugins/builtin/broker-manager/index.tsx +3 -0
- package/src/plugins/builtin/buildout/model/index.ts +1 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +17 -18
- package/src/plugins/builtin/cds/client.test.ts +83 -0
- package/src/plugins/builtin/cds/client.ts +67 -0
- package/src/plugins/builtin/cds/index.tsx +41 -0
- package/src/plugins/builtin/cds/model.test.ts +232 -0
- package/src/plugins/builtin/cds/model.ts +401 -0
- package/src/plugins/builtin/cds/pane.test.tsx +132 -0
- package/src/plugins/builtin/cds/pane.tsx +358 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +31 -0
- package/src/plugins/builtin/chart-composer/index.tsx +88 -2
- package/src/plugins/builtin/chart-composer/pane-share.test.ts +71 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +103 -15
- package/src/plugins/builtin/chat/content/index.tsx +4 -1
- package/src/plugins/builtin/chat/controller/channels.ts +7 -1
- package/src/plugins/builtin/chat/controller/index.ts +14 -9
- package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
- package/src/plugins/builtin/chat/controller/persistence.ts +0 -3
- package/src/plugins/builtin/chat/controller/realtime.ts +3 -3
- package/src/plugins/builtin/chat/controller/send.ts +1 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +18 -11
- package/src/plugins/builtin/chat/controller/state.ts +1 -3
- package/src/plugins/builtin/chat/controller/storage.ts +0 -2
- package/src/plugins/builtin/chat/controller/utils.ts +4 -6
- package/src/plugins/builtin/chat/controller/view.ts +2 -2
- package/src/plugins/builtin/chat/controller.test.ts +142 -14
- package/src/plugins/builtin/chat/message/desktop.test.tsx +88 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +3 -1
- package/src/plugins/builtin/chat/message/profile-popover.test.tsx +1 -29
- package/src/plugins/builtin/chat/profile-popover.ts +53 -22
- package/src/plugins/builtin/chat/sidebar.test.tsx +81 -0
- package/src/plugins/builtin/chat/sidebar.tsx +63 -0
- package/src/plugins/builtin/chat/test-harness.tsx +29 -1
- package/src/plugins/builtin/cloud/auth-actions.tsx +14 -7
- package/src/plugins/builtin/cloud/auth-commands.ts +21 -51
- package/src/plugins/builtin/cloud/auth-dialog.tsx +296 -0
- package/src/{components/onboarding/account-step/model.test.ts → plugins/builtin/cloud/auth-model.test.ts} +1 -1
- package/src/{components/onboarding/account-step/model.ts → plugins/builtin/cloud/auth-model.ts} +37 -3
- package/src/plugins/builtin/cloud/browser.tsx +9 -0
- package/src/plugins/builtin/cloud/buildout-module.tsx +23 -0
- package/src/plugins/builtin/cloud/device-signin.ts +2 -8
- package/src/plugins/builtin/cloud/index.tsx +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +14 -24
- package/src/plugins/builtin/cloud/upgrade-command.ts +13 -6
- package/src/plugins/builtin/cloud-tweets/pane-share.test.ts +56 -0
- package/src/plugins/builtin/cloud-tweets/pane.tsx +20 -5
- package/src/plugins/builtin/cloud-tweets/registration.ts +50 -6
- package/src/plugins/builtin/composite-plugins.ts +3 -1
- package/src/plugins/builtin/congress-trades/detail.tsx +43 -1
- package/src/plugins/builtin/congress-trades/model.test.ts +85 -0
- package/src/plugins/builtin/congress-trades/model.ts +69 -1
- package/src/plugins/builtin/congress-trades/pane.tsx +51 -2
- package/src/plugins/builtin/connections/index.ts +3 -0
- package/src/plugins/builtin/dividend-yield/client.ts +18 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +7 -6
- package/src/plugins/builtin/earnings/index.tsx +5 -3
- package/src/plugins/builtin/earnings/model.test.ts +19 -1
- package/src/plugins/builtin/earnings/model.ts +17 -15
- package/src/plugins/builtin/econ/calendar-model.ts +0 -5
- package/src/plugins/builtin/econ/index.test.tsx +6 -1
- package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
- package/src/plugins/builtin/help/index.tsx +2 -2
- package/src/plugins/builtin/holders/thirteenf-match.ts +1 -2
- package/src/plugins/builtin/insider/index.tsx +27 -11
- package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
- package/src/plugins/builtin/layout-manager/index.tsx +17 -4
- package/src/plugins/builtin/news/index.tsx +11 -6
- package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +2 -0
- package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +2 -0
- package/src/plugins/builtin/news/wire/breaking/pane.tsx +4 -1
- package/src/plugins/builtin/news/wire/feed-config.test.ts +15 -1
- package/src/plugins/builtin/news/wire/feed-config.ts +25 -5
- package/src/plugins/builtin/news/wire/index.tsx +3 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +7 -2
- package/src/plugins/builtin/news/wire/news/footer.ts +34 -1
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +4 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +8 -2
- package/src/plugins/builtin/news/wire/rss/source.test.ts +12 -0
- package/src/plugins/builtin/news/wire/rss/source.ts +1 -1
- package/src/plugins/builtin/notes/files.ts +80 -0
- package/src/plugins/builtin/notes/index.tsx +3 -0
- package/src/plugins/builtin/notes/sync.test.ts +49 -0
- package/src/plugins/builtin/notes/sync.ts +98 -0
- package/src/plugins/builtin/options/index.tsx +1 -0
- package/src/plugins/builtin/options/view.test.tsx +25 -3
- package/src/plugins/builtin/options/view.tsx +1 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +22 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +12 -7
- package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
- package/src/plugins/builtin/portfolio-list/column-values.ts +15 -4
- package/src/plugins/builtin/portfolio-list/header.tsx +13 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +8 -0
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
- package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +1 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +6 -1
- package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +35 -21
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -12
- package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +329 -0
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +582 -0
- package/src/plugins/builtin/research/index.test.ts +14 -0
- package/src/plugins/builtin/research/index.tsx +29 -0
- package/src/plugins/builtin/sec/index.tsx +24 -9
- package/src/plugins/builtin/sectors/index.tsx +14 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +18 -11
- package/src/plugins/builtin/shared/public-pane.test.ts +12 -0
- package/src/plugins/builtin/shared/public-pane.ts +9 -0
- package/src/plugins/builtin/shared/public-share.ts +23 -0
- package/src/plugins/builtin/shared/ticker-source.ts +32 -0
- package/src/plugins/builtin/shared/ticker-surface.test.ts +27 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +17 -0
- package/src/plugins/builtin/substack/api/cache.ts +7 -3
- package/src/plugins/builtin/substack/api/loaders.ts +36 -12
- package/src/plugins/builtin/substack/api/types.ts +2 -0
- package/src/plugins/builtin/substack/pane-state.ts +1 -1
- package/src/plugins/builtin/substack/pane.tsx +55 -12
- package/src/plugins/builtin/thirteenf/api.test.ts +1 -1
- package/src/plugins/builtin/thirteenf/api.ts +40 -18
- package/src/plugins/builtin/thirteenf/data.ts +15 -7
- package/src/plugins/builtin/thirteenf/pane.tsx +45 -10
- package/src/plugins/builtin/ticker-detail/index.tsx +10 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +12 -4
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -2
- package/src/plugins/builtin/ticker-detail/settings.ts +12 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +2 -0
- package/src/plugins/builtin/world-indices/index.tsx +2 -0
- package/src/plugins/catalog-browser.test.ts +78 -0
- package/src/plugins/catalog-browser.ts +136 -0
- package/src/plugins/gridlock-notification.ts +2 -1
- package/src/plugins/ibkr/flex/index.test.ts +19 -0
- package/src/plugins/ibkr/flex/index.ts +1 -1
- package/src/plugins/pane-manager/gridlock.ts +87 -3
- package/src/plugins/pane-manager.test.ts +30 -0
- package/src/plugins/pane-manager.ts +6 -1
- package/src/plugins/prediction-markets/chart.tsx +2 -2
- package/src/plugins/prediction-markets/controller/catalog.ts +77 -2
- package/src/plugins/prediction-markets/controller/data.ts +7 -1
- package/src/plugins/prediction-markets/controller/index.ts +9 -2
- package/src/plugins/prediction-markets/controller/keyboard.ts +10 -14
- package/src/plugins/prediction-markets/detail/overview.tsx +21 -0
- package/src/plugins/prediction-markets/detail/pane.tsx +43 -34
- package/src/plugins/prediction-markets/detail/trades.tsx +34 -9
- package/src/plugins/prediction-markets/detail.test.tsx +29 -2
- package/src/plugins/prediction-markets/index.tsx +8 -4
- package/src/plugins/prediction-markets/navigation.ts +0 -1
- package/src/plugins/prediction-markets/pane.test.tsx +77 -0
- package/src/plugins/prediction-markets/pane.tsx +12 -0
- package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
- package/src/plugins/prediction-markets/services/kalshi/adapter.ts +49 -9
- package/src/plugins/prediction-markets/services/polymarket/adapter.ts +31 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
- package/src/plugins/registry/index.ts +2 -0
- package/src/plugins/ticker-navigation.test.ts +24 -0
- package/src/plugins/ticker-navigation.ts +40 -0
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/app-services.ts +61 -0
- package/src/renderers/browser/cloud-transport.test.ts +68 -0
- package/src/renderers/browser/cloud-transport.ts +48 -0
- package/src/renderers/browser/config-host.ts +58 -0
- package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
- package/src/renderers/browser/deeplink-bridge.ts +22 -0
- package/src/renderers/browser/error-boundary.tsx +23 -0
- package/src/renderers/browser/main.tsx +64 -0
- package/src/renderers/browser/persistence.ts +74 -0
- package/src/renderers/browser/storage.test.ts +66 -0
- package/src/renderers/browser/storage.ts +50 -0
- package/src/renderers/browser/ticker-repository.ts +59 -0
- package/src/renderers/browser/ui-host.tsx +40 -0
- package/src/renderers/cloudflare/worker.test.ts +136 -0
- package/src/renderers/cloudflare/worker.ts +116 -0
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +7 -1
- package/src/renderers/electrobun/view/app-services.ts +14 -3
- package/src/renderers/electrobun/view/build-assets.ts +12 -10
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/data-table/index.tsx +10 -1
- package/src/renderers/electrobun/view/desktop/controls.tsx +2 -0
- package/src/renderers/electrobun/view/dom-ui-host.tsx +143 -0
- package/src/renderers/electrobun/view/host/box.test.tsx +33 -0
- package/src/renderers/electrobun/view/host/box.tsx +11 -6
- package/src/renderers/electrobun/view/host/chart-surface.tsx +7 -1
- package/src/renderers/electrobun/view/host/input.tsx +11 -7
- package/src/renderers/electrobun/view/host/native.ts +0 -5
- package/src/renderers/electrobun/view/main.tsx +2 -0
- package/src/renderers/electrobun/view/remote/persistence.ts +5 -0
- package/src/renderers/electrobun/view/styles.css +107 -7
- package/src/renderers/electrobun/view/toast-host.test.tsx +78 -0
- package/src/renderers/electrobun/view/toast-host.tsx +69 -43
- package/src/renderers/electrobun/view/ui-host.tsx +6 -137
- package/src/renderers/opentui/start.tsx +2 -0
- package/src/renderers/share/layout-view.test.tsx +58 -0
- package/src/renderers/share/layout-view.tsx +133 -0
- package/src/renderers/share/main.tsx +102 -0
- package/src/renderers/share/styles.css +54 -0
- package/src/renderers/share/view.test.tsx +100 -0
- package/src/renderers/share/view.tsx +150 -0
- package/src/shares/api.test.ts +105 -0
- package/src/shares/api.ts +127 -0
- package/src/shares/chart-snapshot.test.ts +34 -0
- package/src/shares/chart-snapshot.ts +28 -0
- package/src/shares/location.test.ts +19 -0
- package/src/shares/location.ts +11 -0
- package/src/shares/pane.test.ts +110 -0
- package/src/shares/pane.ts +54 -0
- package/src/shares/payload.ts +177 -0
- package/src/sources/gloomberb-cloud/index.test.ts +61 -1
- package/src/sources/gloomberb-cloud/index.ts +86 -14
- package/src/sources/gloomberb-cloud/normalizers.test.ts +34 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +5 -5
- package/src/sources/provider-router/history.test.ts +2 -2
- package/src/sources/provider-router/news.test.ts +34 -0
- package/src/sources/provider-router/news.ts +15 -1
- package/src/sources/sec-edgar.test.ts +59 -0
- package/src/sources/sec-edgar.ts +63 -23
- package/src/sources/yahoo-finance/mappers.ts +3 -0
- package/src/sources/yahoo-finance/snapshots.ts +10 -2
- package/src/sources/yahoo-finance/symbols.test.ts +10 -0
- package/src/sources/yahoo-finance/symbols.ts +13 -8
- package/src/sources/yahoo-finance.test.ts +1 -1
- package/src/sources/yahoo-finance.ts +6 -2
- package/src/state/app/bootstrap.ts +6 -7
- package/src/state/app/context/index.test.ts +41 -1
- package/src/state/app/context/index.test.tsx +34 -16
- package/src/state/selectors-ui.ts +0 -8
- package/src/sync/baseline.ts +32 -0
- package/src/sync/controller.test.ts +124 -1
- package/src/sync/controller.ts +54 -12
- package/src/sync/core-contributors.test.ts +44 -0
- package/src/sync/core-contributors.ts +59 -10
- package/src/sync/react.ts +22 -2
- package/src/sync/types.ts +12 -0
- package/src/theme/font-scale.ts +0 -11
- package/src/tickers/search/index.test.ts +27 -0
- package/src/tickers/search/upsert.ts +10 -0
- package/src/types/capability-route-source.ts +2 -1
- package/src/types/config.ts +35 -5
- package/src/types/desktop-menu.ts +1 -0
- package/src/types/plugin.ts +36 -0
- package/src/ui/context-menu.tsx +1 -1
- package/src/ui/host.tsx +4 -0
- package/src/ui/toast.tsx +2 -0
- package/src/utils/back-navigation.ts +0 -4
- package/src/utils/price-history.ts +11 -10
- package/src/utils/sec.test.ts +6 -0
- package/src/utils/sec.ts +1 -1
|
@@ -422,21 +422,121 @@ body.gloom-dragging [data-gloom-role="chart-surface"] {
|
|
|
422
422
|
right: 16px;
|
|
423
423
|
bottom: 16px;
|
|
424
424
|
display: flex;
|
|
425
|
+
width: min(380px, calc(100vw - 32px));
|
|
425
426
|
flex-direction: column;
|
|
426
427
|
gap: 8px;
|
|
428
|
+
pointer-events: none;
|
|
427
429
|
z-index: 10000;
|
|
428
430
|
}
|
|
429
431
|
.gloom-toast {
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
432
|
+
--gloom-toast-accent: var(--gloom-border-focused);
|
|
433
|
+
display: grid;
|
|
434
|
+
grid-template-columns: 8px minmax(0, 1fr) auto;
|
|
435
|
+
gap: 10px;
|
|
436
|
+
align-items: start;
|
|
437
|
+
width: 100%;
|
|
438
|
+
padding: 11px 10px 11px 12px;
|
|
439
|
+
overflow: hidden;
|
|
440
|
+
border: 1px solid color-mix(in srgb, var(--gloom-toast-accent) 54%, var(--gloom-border));
|
|
441
|
+
border-radius: 7px;
|
|
442
|
+
background: color-mix(in srgb, var(--gloom-panel) 94%, var(--gloom-bg));
|
|
443
|
+
color: var(--gloom-text);
|
|
444
|
+
box-shadow: 0 14px 32px color-mix(in srgb, var(--gloom-bg) 42%, transparent);
|
|
445
|
+
pointer-events: auto;
|
|
446
|
+
}
|
|
447
|
+
.gloom-toast[data-type="success"] { --gloom-toast-accent: var(--gloom-positive); }
|
|
448
|
+
.gloom-toast[data-type="error"] { --gloom-toast-accent: var(--gloom-negative); }
|
|
449
|
+
.gloom-toast[data-actionable="true"] {
|
|
450
|
+
cursor: pointer;
|
|
451
|
+
transition: transform 120ms cubic-bezier(0.23, 1, 0.32, 1), border-color 120ms ease, background-color 120ms ease;
|
|
452
|
+
}
|
|
453
|
+
.gloom-toast[data-actionable="true"]:active { transform: scale(0.985); }
|
|
454
|
+
.gloom-toast-indicator {
|
|
455
|
+
width: 8px;
|
|
456
|
+
height: 8px;
|
|
457
|
+
margin-top: 5px;
|
|
458
|
+
border-radius: 50%;
|
|
459
|
+
background: var(--gloom-toast-accent);
|
|
460
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--gloom-toast-accent) 14%, transparent);
|
|
461
|
+
}
|
|
462
|
+
.gloom-toast-content { min-width: 0; }
|
|
463
|
+
.gloom-toast-heading {
|
|
464
|
+
display: flex;
|
|
465
|
+
min-width: 0;
|
|
466
|
+
margin-bottom: 2px;
|
|
467
|
+
align-items: baseline;
|
|
468
|
+
gap: 7px;
|
|
469
|
+
}
|
|
470
|
+
.gloom-toast-title {
|
|
471
|
+
overflow: hidden;
|
|
472
|
+
color: var(--gloom-text-bright);
|
|
473
|
+
font-weight: 700;
|
|
474
|
+
text-overflow: ellipsis;
|
|
475
|
+
white-space: nowrap;
|
|
476
|
+
}
|
|
477
|
+
.gloom-toast-subtitle {
|
|
478
|
+
overflow: hidden;
|
|
479
|
+
color: var(--gloom-text-dim);
|
|
480
|
+
font-size: 11px;
|
|
481
|
+
text-overflow: ellipsis;
|
|
482
|
+
white-space: nowrap;
|
|
483
|
+
}
|
|
484
|
+
.gloom-toast-body {
|
|
485
|
+
color: var(--gloom-text);
|
|
486
|
+
line-height: 1.45;
|
|
487
|
+
overflow-wrap: anywhere;
|
|
488
|
+
}
|
|
489
|
+
.gloom-toast-controls {
|
|
490
|
+
display: flex;
|
|
491
|
+
align-items: center;
|
|
492
|
+
gap: 4px;
|
|
493
|
+
}
|
|
494
|
+
.gloom-toast-action,
|
|
495
|
+
.gloom-toast-dismiss {
|
|
496
|
+
appearance: none;
|
|
497
|
+
border: 1px solid transparent;
|
|
498
|
+
background: transparent;
|
|
499
|
+
color: var(--gloom-text-dim);
|
|
435
500
|
}
|
|
436
501
|
.gloom-toast-action {
|
|
437
|
-
|
|
438
|
-
|
|
502
|
+
padding: 3px 7px;
|
|
503
|
+
border-color: color-mix(in srgb, var(--gloom-toast-accent) 42%, var(--gloom-border));
|
|
504
|
+
border-radius: 4px;
|
|
505
|
+
color: var(--gloom-text-bright);
|
|
506
|
+
}
|
|
507
|
+
.gloom-toast-dismiss {
|
|
508
|
+
display: grid;
|
|
509
|
+
width: 22px;
|
|
510
|
+
height: 22px;
|
|
511
|
+
padding: 0;
|
|
439
512
|
border-radius: 4px;
|
|
513
|
+
place-items: center;
|
|
514
|
+
}
|
|
515
|
+
.gloom-toast-dismiss svg {
|
|
516
|
+
fill: none;
|
|
517
|
+
stroke: currentColor;
|
|
518
|
+
stroke-linecap: round;
|
|
519
|
+
stroke-width: 1.5;
|
|
520
|
+
}
|
|
521
|
+
.gloom-toast-action:focus-visible,
|
|
522
|
+
.gloom-toast-dismiss:focus-visible {
|
|
523
|
+
outline: 1px solid var(--gloom-border-focused);
|
|
524
|
+
outline-offset: 1px;
|
|
525
|
+
}
|
|
526
|
+
@media (hover: hover) and (pointer: fine) {
|
|
527
|
+
.gloom-toast[data-actionable="true"]:hover {
|
|
528
|
+
border-color: var(--gloom-toast-accent);
|
|
529
|
+
background: color-mix(in srgb, var(--gloom-panel) 88%, var(--gloom-toast-accent));
|
|
530
|
+
}
|
|
531
|
+
.gloom-toast-action:hover,
|
|
532
|
+
.gloom-toast-dismiss:hover {
|
|
533
|
+
background: color-mix(in srgb, var(--gloom-text) 10%, transparent);
|
|
534
|
+
color: var(--gloom-text-bright);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
@media (prefers-reduced-motion: reduce) {
|
|
538
|
+
.gloom-toast[data-actionable="true"] { transition: none; }
|
|
539
|
+
.gloom-toast[data-actionable="true"]:active { transform: none; }
|
|
440
540
|
}
|
|
441
541
|
.gloom-dialog-backdrop {
|
|
442
542
|
position: fixed;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** @jsxImportSource react */
|
|
2
|
+
import { Window } from "happy-dom";
|
|
3
|
+
|
|
4
|
+
const testWindow = new Window({ url: "http://localhost" });
|
|
5
|
+
Object.assign(globalThis, {
|
|
6
|
+
IS_REACT_ACT_ENVIRONMENT: true,
|
|
7
|
+
window: testWindow,
|
|
8
|
+
document: testWindow.document,
|
|
9
|
+
navigator: testWindow.navigator,
|
|
10
|
+
HTMLElement: testWindow.HTMLElement,
|
|
11
|
+
MouseEvent: testWindow.MouseEvent,
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
import { expect, test } from "bun:test";
|
|
15
|
+
import { act } from "react";
|
|
16
|
+
import { createRoot } from "react-dom/client";
|
|
17
|
+
import { useToastHost, type ToastHost } from "../../../ui/toast";
|
|
18
|
+
import { WebToastHostProvider } from "./toast-host";
|
|
19
|
+
|
|
20
|
+
let toastHost: ToastHost | null = null;
|
|
21
|
+
|
|
22
|
+
function ToastViewport() {
|
|
23
|
+
const host = useToastHost();
|
|
24
|
+
toastHost = host;
|
|
25
|
+
const Viewport = host.Viewport;
|
|
26
|
+
return <Viewport />;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
test("DOM notifications show context and open from the whole card", async () => {
|
|
30
|
+
const container = testWindow.document.createElement("div");
|
|
31
|
+
testWindow.document.body.appendChild(container);
|
|
32
|
+
const root = createRoot(container as unknown as HTMLElement);
|
|
33
|
+
let opened = 0;
|
|
34
|
+
|
|
35
|
+
await act(async () => {
|
|
36
|
+
root.render(
|
|
37
|
+
<WebToastHostProvider>
|
|
38
|
+
<ToastViewport />
|
|
39
|
+
</WebToastHostProvider>,
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
await act(async () => {
|
|
43
|
+
toastHost?.info("@bob mentioned you", {
|
|
44
|
+
title: "Gloomberb chat",
|
|
45
|
+
subtitle: "#everyone",
|
|
46
|
+
duration: 0,
|
|
47
|
+
action: { label: "Open", onClick: () => opened++ },
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const toast = container.querySelector(".gloom-toast") as unknown as HTMLElement;
|
|
52
|
+
expect(toast.textContent).toContain("Gloomberb chat");
|
|
53
|
+
expect(toast.textContent).toContain("#everyone");
|
|
54
|
+
expect(toast.getAttribute("data-actionable")).toBe("true");
|
|
55
|
+
|
|
56
|
+
await act(async () => {
|
|
57
|
+
toast.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
58
|
+
});
|
|
59
|
+
expect(opened).toBe(1);
|
|
60
|
+
expect(container.querySelector(".gloom-toast")).toBeNull();
|
|
61
|
+
|
|
62
|
+
await act(async () => {
|
|
63
|
+
toastHost?.info("Another message", {
|
|
64
|
+
duration: 0,
|
|
65
|
+
action: { label: "Open", onClick: () => opened++ },
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
const dismiss = container.querySelector(".gloom-toast-dismiss") as unknown as HTMLElement;
|
|
69
|
+
await act(async () => {
|
|
70
|
+
dismiss.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
|
71
|
+
});
|
|
72
|
+
expect(opened).toBe(1);
|
|
73
|
+
expect(container.querySelector(".gloom-toast")).toBeNull();
|
|
74
|
+
|
|
75
|
+
await act(async () => root.unmount());
|
|
76
|
+
container.remove();
|
|
77
|
+
toastHost = null;
|
|
78
|
+
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/** @jsxImportSource react */
|
|
2
|
-
import { useCallback, useMemo, useState, type
|
|
3
|
-
import { colors } from "../../../theme/colors";
|
|
2
|
+
import { useCallback, useMemo, useState, type ReactNode } from "react";
|
|
4
3
|
import { ToastHostProvider, type ToastHost, type ToastOptions } from "../../../ui/toast";
|
|
5
4
|
|
|
6
5
|
type WebToastType = "info" | "success" | "error";
|
|
@@ -9,33 +8,13 @@ interface ToastEntry {
|
|
|
9
8
|
id: number;
|
|
10
9
|
body: string;
|
|
11
10
|
type: WebToastType;
|
|
11
|
+
title?: string;
|
|
12
|
+
subtitle?: string;
|
|
12
13
|
action?: ToastOptions["action"];
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
let nextToastId = 1;
|
|
16
17
|
|
|
17
|
-
function webToastBorderColor(type: WebToastType): string {
|
|
18
|
-
if (type === "success") return colors.positive;
|
|
19
|
-
if (type === "error") return colors.negative;
|
|
20
|
-
return colors.borderFocused;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function getToastStyle(type: WebToastType): CSSProperties {
|
|
24
|
-
return {
|
|
25
|
-
background: colors.panel,
|
|
26
|
-
borderColor: webToastBorderColor(type),
|
|
27
|
-
color: colors.text,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function getToastActionStyle(): CSSProperties {
|
|
32
|
-
return {
|
|
33
|
-
background: colors.selected,
|
|
34
|
-
border: `1px solid ${colors.borderFocused}`,
|
|
35
|
-
color: colors.selectedText,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
18
|
export function WebToastHostProvider({ children }: { children: ReactNode }) {
|
|
40
19
|
const [toasts, setToasts] = useState<ToastEntry[]>([]);
|
|
41
20
|
|
|
@@ -45,7 +24,14 @@ export function WebToastHostProvider({ children }: { children: ReactNode }) {
|
|
|
45
24
|
|
|
46
25
|
const push = useCallback((type: ToastEntry["type"], body: string, options?: ToastOptions) => {
|
|
47
26
|
const id = nextToastId++;
|
|
48
|
-
setToasts((current) => [...current, {
|
|
27
|
+
setToasts((current) => [...current, {
|
|
28
|
+
id,
|
|
29
|
+
type,
|
|
30
|
+
body,
|
|
31
|
+
title: options?.title,
|
|
32
|
+
subtitle: options?.subtitle,
|
|
33
|
+
action: options?.action,
|
|
34
|
+
}]);
|
|
49
35
|
if (options?.duration !== 0) {
|
|
50
36
|
setTimeout(() => dismiss(id), options?.duration ?? 4500);
|
|
51
37
|
}
|
|
@@ -55,24 +41,64 @@ export function WebToastHostProvider({ children }: { children: ReactNode }) {
|
|
|
55
41
|
const host = useMemo<ToastHost>(() => ({
|
|
56
42
|
Viewport() {
|
|
57
43
|
return (
|
|
58
|
-
<div className="gloom-toast-viewport">
|
|
59
|
-
{toasts.map((toast) =>
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
{
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
44
|
+
<div className="gloom-toast-viewport" aria-label="Notifications" aria-live="polite">
|
|
45
|
+
{toasts.map((toast) => {
|
|
46
|
+
const activate = () => {
|
|
47
|
+
if (!toast.action) return;
|
|
48
|
+
try {
|
|
49
|
+
toast.action.onClick();
|
|
50
|
+
} finally {
|
|
51
|
+
dismiss(toast.id);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
return (
|
|
55
|
+
<div
|
|
56
|
+
key={toast.id}
|
|
57
|
+
className="gloom-toast"
|
|
58
|
+
data-type={toast.type}
|
|
59
|
+
data-actionable={toast.action ? "true" : "false"}
|
|
60
|
+
onClick={toast.action ? activate : undefined}
|
|
61
|
+
>
|
|
62
|
+
<span className="gloom-toast-indicator" aria-hidden="true" />
|
|
63
|
+
<div className="gloom-toast-content">
|
|
64
|
+
{(toast.title || toast.subtitle) && (
|
|
65
|
+
<div className="gloom-toast-heading">
|
|
66
|
+
{toast.title && <span className="gloom-toast-title">{toast.title}</span>}
|
|
67
|
+
{toast.subtitle && <span className="gloom-toast-subtitle">{toast.subtitle}</span>}
|
|
68
|
+
</div>
|
|
69
|
+
)}
|
|
70
|
+
<div className="gloom-toast-body">{toast.body}</div>
|
|
71
|
+
</div>
|
|
72
|
+
<div className="gloom-toast-controls">
|
|
73
|
+
{toast.action && (
|
|
74
|
+
<button
|
|
75
|
+
type="button"
|
|
76
|
+
className="gloom-toast-action"
|
|
77
|
+
onClick={(event) => {
|
|
78
|
+
event.stopPropagation();
|
|
79
|
+
activate();
|
|
80
|
+
}}
|
|
81
|
+
>
|
|
82
|
+
{toast.action.label}
|
|
83
|
+
</button>
|
|
84
|
+
)}
|
|
85
|
+
<button
|
|
86
|
+
type="button"
|
|
87
|
+
className="gloom-toast-dismiss"
|
|
88
|
+
aria-label="Dismiss notification"
|
|
89
|
+
onClick={(event) => {
|
|
90
|
+
event.stopPropagation();
|
|
91
|
+
dismiss(toast.id);
|
|
92
|
+
}}
|
|
93
|
+
>
|
|
94
|
+
<svg viewBox="0 0 12 12" width="12" height="12" aria-hidden="true">
|
|
95
|
+
<path d="M2.25 2.25 9.75 9.75M9.75 2.25 2.25 9.75" />
|
|
96
|
+
</svg>
|
|
97
|
+
</button>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
);
|
|
101
|
+
})}
|
|
76
102
|
</div>
|
|
77
103
|
);
|
|
78
104
|
},
|
|
@@ -1,149 +1,18 @@
|
|
|
1
1
|
/** @jsxImportSource react */
|
|
2
|
-
import { useEffect, useState, type CSSProperties, type ReactNode } from "react";
|
|
3
2
|
import type { RendererHost, UiHost } from "../../../ui/host";
|
|
4
|
-
import { WEB_CELL_HEIGHT, WEB_CELL_WIDTH } from "./input-host";
|
|
5
3
|
import { backendRequest } from "./backend-rpc";
|
|
6
|
-
import { WebDataTable } from "./data-table";
|
|
7
|
-
import {
|
|
8
|
-
WebButton,
|
|
9
|
-
WebCheckbox,
|
|
10
|
-
WebDialogFrame,
|
|
11
|
-
WebListView,
|
|
12
|
-
WebMessageComposer,
|
|
13
|
-
WebPageStackView,
|
|
14
|
-
WebSegmentedControl,
|
|
15
|
-
WebTextField,
|
|
16
|
-
} from "./desktop/controls";
|
|
17
|
-
import { WebPopover } from "./desktop/popover";
|
|
18
|
-
import { WebBox } from "./host/box";
|
|
19
|
-
import { WebChartSurface } from "./host/chart-surface";
|
|
20
|
-
import { WebInput, WebTextarea } from "./host/input";
|
|
21
|
-
import { WebMediaSurface } from "./host/media-surface";
|
|
22
4
|
import {
|
|
23
5
|
NATIVE_CONTEXT_MENU_SUPPORTED,
|
|
24
6
|
showDesktopContextMenu,
|
|
25
7
|
startElectrobunWindowDrag,
|
|
26
8
|
} from "./host/native";
|
|
27
|
-
import {
|
|
28
|
-
import { cleanDomProps, commonStyle } from "./host/style";
|
|
29
|
-
import { WebAsciiText, WebSpan, WebStrong, WebText, WebUnderline } from "./host/text";
|
|
30
|
-
import { WebTabs } from "./host/tabs";
|
|
31
|
-
|
|
32
|
-
function currentDesktopPlatform(): string {
|
|
33
|
-
const navigatorWithUserAgentData = navigator as Navigator & {
|
|
34
|
-
userAgentData?: { platform?: string };
|
|
35
|
-
};
|
|
36
|
-
return [
|
|
37
|
-
navigatorWithUserAgentData.userAgentData?.platform,
|
|
38
|
-
navigator.platform,
|
|
39
|
-
navigator.userAgent,
|
|
40
|
-
].filter((value): value is string => Boolean(value)).join(" ");
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const DESKTOP_PLATFORM = currentDesktopPlatform();
|
|
44
|
-
const USES_WINDOWS_CONTROLS = !/(darwin|ipad|iphone|linux|mac)/i.test(DESKTOP_PLATFORM);
|
|
45
|
-
const NON_WINDOWS_DESKTOP_PLATFORMS = /^(darwin|linux|freebsd|openbsd|aix|sunos)$/i;
|
|
46
|
-
|
|
47
|
-
function usesWindowsWindowControls(desktopPlatform?: string): boolean {
|
|
48
|
-
const platform = desktopPlatform?.trim();
|
|
49
|
-
if (!platform) {
|
|
50
|
-
return USES_WINDOWS_CONTROLS;
|
|
51
|
-
}
|
|
52
|
-
if (/^win/i.test(platform)) {
|
|
53
|
-
return true;
|
|
54
|
-
}
|
|
55
|
-
if (NON_WINDOWS_DESKTOP_PLATFORMS.test(platform)) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
return USES_WINDOWS_CONTROLS;
|
|
59
|
-
}
|
|
9
|
+
import { createDomUiHost } from "./dom-ui-host";
|
|
60
10
|
|
|
61
11
|
export function createWebUiHost(desktopPlatform?: string): UiHost {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
capabilities: {
|
|
67
|
-
nativePaneChrome: true,
|
|
68
|
-
titleBarOverlay: true,
|
|
69
|
-
precisePointer: true,
|
|
70
|
-
fractionalViewport: true,
|
|
71
|
-
cellWidthPx: WEB_CELL_WIDTH,
|
|
72
|
-
cellHeightPx: WEB_CELL_HEIGHT,
|
|
73
|
-
pixelRatio: Math.min(window.devicePixelRatio || 1, 2),
|
|
74
|
-
canvasCharts: true,
|
|
75
|
-
nativeContextMenu: NATIVE_CONTEXT_MENU_SUPPORTED,
|
|
76
|
-
windowControls: usesWindowsControls ? "windows" : undefined,
|
|
77
|
-
},
|
|
78
|
-
Box: WebBox,
|
|
79
|
-
Text: WebText,
|
|
80
|
-
Span: WebSpan,
|
|
81
|
-
Strong: WebStrong,
|
|
82
|
-
Underline: WebUnderline,
|
|
83
|
-
ScrollBox: WebScrollBox,
|
|
84
|
-
Input: WebInput,
|
|
85
|
-
Textarea: WebTextarea,
|
|
86
|
-
Button: WebButton,
|
|
87
|
-
Checkbox: WebCheckbox,
|
|
88
|
-
Popover: WebPopover,
|
|
89
|
-
TextField: WebTextField,
|
|
90
|
-
MessageComposer: WebMessageComposer,
|
|
91
|
-
ListView: WebListView,
|
|
92
|
-
SegmentedControl: WebSegmentedControl,
|
|
93
|
-
DialogFrame: WebDialogFrame,
|
|
94
|
-
PageStackView: WebPageStackView,
|
|
95
|
-
DataTable: WebDataTable,
|
|
96
|
-
Tabs: WebTabs,
|
|
97
|
-
ChartSurface: WebChartSurface,
|
|
98
|
-
ImageSurface: ({ children, src, alt = "", objectFit = "contain", ...props }) => {
|
|
99
|
-
const imageSrc = typeof src === "string" ? src.trim() : "";
|
|
100
|
-
const [failed, setFailed] = useState(false);
|
|
101
|
-
useEffect(() => setFailed(false), [imageSrc]);
|
|
102
|
-
const baseStyle = commonStyle(props);
|
|
103
|
-
return (
|
|
104
|
-
<div
|
|
105
|
-
{...cleanDomProps(props)}
|
|
106
|
-
style={{
|
|
107
|
-
...baseStyle,
|
|
108
|
-
overflow: baseStyle.overflow ?? "hidden",
|
|
109
|
-
...(props.style as CSSProperties | undefined),
|
|
110
|
-
}}
|
|
111
|
-
>
|
|
112
|
-
{imageSrc && !failed ? (
|
|
113
|
-
<img
|
|
114
|
-
src={imageSrc}
|
|
115
|
-
alt={alt}
|
|
116
|
-
draggable={false}
|
|
117
|
-
onError={() => setFailed(true)}
|
|
118
|
-
style={{
|
|
119
|
-
width: "100%",
|
|
120
|
-
height: "100%",
|
|
121
|
-
display: "block",
|
|
122
|
-
objectFit,
|
|
123
|
-
}}
|
|
124
|
-
/>
|
|
125
|
-
) : children as ReactNode}
|
|
126
|
-
</div>
|
|
127
|
-
);
|
|
128
|
-
},
|
|
129
|
-
MediaSurface: WebMediaSurface,
|
|
130
|
-
SpinnerMark: ({ color, ...props }) => (
|
|
131
|
-
<span
|
|
132
|
-
{...cleanDomProps(props)}
|
|
133
|
-
aria-hidden="true"
|
|
134
|
-
style={{
|
|
135
|
-
color,
|
|
136
|
-
display: "inline-block",
|
|
137
|
-
width: "1ch",
|
|
138
|
-
animation: "gloom-spin 0.9s steps(8) infinite",
|
|
139
|
-
...(props.style as CSSProperties | undefined),
|
|
140
|
-
}}
|
|
141
|
-
>
|
|
142
|
-
*
|
|
143
|
-
</span>
|
|
144
|
-
),
|
|
145
|
-
AsciiText: (props) => <WebAsciiText {...props} desktopPlatform={desktopPlatform} />,
|
|
146
|
-
};
|
|
12
|
+
return createDomUiHost(desktopPlatform, {
|
|
13
|
+
nativeContextMenu: NATIVE_CONTEXT_MENU_SUPPORTED,
|
|
14
|
+
publicSharing: true,
|
|
15
|
+
});
|
|
147
16
|
}
|
|
148
17
|
|
|
149
18
|
export const webUiHost: UiHost = createWebUiHost();
|
|
@@ -189,7 +58,7 @@ export const webRendererHost: RendererHost = {
|
|
|
189
58
|
try {
|
|
190
59
|
return await navigator.clipboard.readText();
|
|
191
60
|
} catch {
|
|
192
|
-
return
|
|
61
|
+
return backendRequest("host.readText");
|
|
193
62
|
}
|
|
194
63
|
},
|
|
195
64
|
notify(notification) {
|
|
@@ -6,6 +6,7 @@ import { getDataDir, initDataDir, setConfigStoreHost } from "../../data/config/s
|
|
|
6
6
|
import { applyLanguageFromConfig } from "../../i18n";
|
|
7
7
|
import * as nodeConfigStoreHost from "../../data/config/store/node";
|
|
8
8
|
import { loadExternalPlugins } from "../../plugins/loader";
|
|
9
|
+
import { getLoadablePlugins } from "../../plugins/catalog";
|
|
9
10
|
import { OpenTuiInputHostProvider } from "./input-host";
|
|
10
11
|
import { debugLog } from "../../utils/debug-log";
|
|
11
12
|
import { UiHostProvider } from "../../ui/host";
|
|
@@ -143,6 +144,7 @@ export async function startOpenTuiApp(options: StartOpenTuiAppOptions = {}): Pro
|
|
|
143
144
|
config={config}
|
|
144
145
|
servicesFactory={createAppServices}
|
|
145
146
|
externalPlugins={externalPlugins}
|
|
147
|
+
plugins={getLoadablePlugins(externalPlugins)}
|
|
146
148
|
cliLaunchRequest={cliLaunchRequest}
|
|
147
149
|
remoteControlAdapter={remoteControlAdapter}
|
|
148
150
|
/>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
3
|
+
import type { LayoutMarketplaceEntry } from "../../layout-marketplace/payload";
|
|
4
|
+
import { LayoutShareView } from "./layout-view";
|
|
5
|
+
|
|
6
|
+
const entry: LayoutMarketplaceEntry = {
|
|
7
|
+
id: "0123456789abcdef0123456789abcdef",
|
|
8
|
+
name: "Social <script>alert(1)</script> Desk",
|
|
9
|
+
schemaVersion: 2,
|
|
10
|
+
sourceConfigVersion: 21,
|
|
11
|
+
layout: {
|
|
12
|
+
dockRoot: {
|
|
13
|
+
kind: "split",
|
|
14
|
+
axis: "horizontal",
|
|
15
|
+
ratio: 0.6,
|
|
16
|
+
first: { kind: "pane", instanceId: "p1" },
|
|
17
|
+
second: { kind: "pane", instanceId: "p2" },
|
|
18
|
+
},
|
|
19
|
+
instances: [
|
|
20
|
+
{
|
|
21
|
+
instanceId: "p1",
|
|
22
|
+
paneId: "twitter-feed",
|
|
23
|
+
params: { query: "$NVDA <img src=x onerror=alert(1)>" },
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
instanceId: "p2",
|
|
27
|
+
paneId: "chart-composer",
|
|
28
|
+
binding: { kind: "fixed", symbol: "NVDA" },
|
|
29
|
+
},
|
|
30
|
+
],
|
|
31
|
+
floating: [],
|
|
32
|
+
detached: [],
|
|
33
|
+
},
|
|
34
|
+
paneState: {},
|
|
35
|
+
author: { username: "analyst", displayName: "Analyst" },
|
|
36
|
+
publishedAt: "2026-08-27T00:00:00.000Z",
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
test("shared layout renders a safe workspace preview and live-copy CTA", () => {
|
|
40
|
+
const html = renderToStaticMarkup(
|
|
41
|
+
<LayoutShareView
|
|
42
|
+
entry={entry}
|
|
43
|
+
openLiveUrl="https://term.gloom.sh/?layout=0123456789abcdef0123456789abcdef"
|
|
44
|
+
/>,
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
expect(html).toContain("Shared via Gloomberb");
|
|
48
|
+
expect(html).toContain("X Feed");
|
|
49
|
+
expect(html).toContain("Chart");
|
|
50
|
+
expect(html).toContain("$NVDA");
|
|
51
|
+
expect(html).toContain("Use this layout");
|
|
52
|
+
expect(html).toContain("independent, editable copy");
|
|
53
|
+
expect(html).toContain('href="https://term.gloom.sh/?layout=0123456789abcdef0123456789abcdef"');
|
|
54
|
+
expect(html).not.toContain("<script>");
|
|
55
|
+
expect(html).not.toContain("<img");
|
|
56
|
+
expect(html).toContain("<script>");
|
|
57
|
+
expect(html).toContain("<img");
|
|
58
|
+
});
|