gloomberb 0.9.9 → 0.10.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 +41 -3
- package/README.zh-CN.md +294 -0
- package/package.json +10 -4
- package/patches/@opentui%2Fcore@0.3.2.patch +26 -69
- package/src/api-client/auth.ts +2 -0
- package/src/api-client/index.test.ts +4 -0
- package/src/api-client/types.ts +3 -0
- package/src/app/pane-runtime/pane-settings-runtime.tsx +1 -0
- package/src/app/pane-template-dialog-wizard.test.tsx +36 -0
- package/src/app/pane-template-dialog-wizard.tsx +10 -3
- package/src/app/runtime/desktop-deeplink.test.ts +15 -0
- package/src/app/runtime/desktop-deeplink.ts +21 -6
- package/src/app.tsx +40 -33
- package/src/capabilities/service-capabilities.ts +7 -2
- package/src/cli/commands/automation.test.ts +202 -0
- package/src/cli/commands/automation.ts +153 -36
- package/src/cli/commands/market.ts +1 -14
- package/src/cli/commands/system.ts +4 -1
- package/src/cli/context.ts +1 -1
- package/src/cli/desktop-pane-shot.ts +65 -4
- package/src/cli/entry.ts +1 -1
- package/src/cli/pane-functions/capabilities.ts +532 -0
- package/src/cli/pane-functions/catalog.ts +39 -4
- package/src/cli/pane-functions/data.test.ts +17 -0
- package/src/cli/pane-functions/data.ts +55 -13
- package/src/cli/pane-functions/discovery.ts +36 -9
- package/src/cli/pane-functions/index.test.ts +104 -1
- package/src/cli/pane-functions/index.ts +64 -5
- package/src/cli/pane-functions/options.ts +21 -6
- package/src/cli/pane-functions/report.ts +735 -72
- package/src/cli/pane-functions/resolver.ts +70 -6
- package/src/cli/pane-functions/screenshot.test.ts +431 -0
- package/src/cli/pane-functions/screenshot.ts +952 -8
- package/src/components/chart/composite/column-layout.ts +215 -0
- package/src/components/chart/composite/composite-chart.test.tsx +630 -0
- package/src/components/chart/composite/composite-chart.tsx +1148 -0
- package/src/components/chart/composite/format.test.ts +137 -0
- package/src/components/chart/composite/format.ts +135 -0
- package/src/components/chart/composite/index.ts +38 -0
- package/src/components/chart/composite/interactions.test.ts +109 -0
- package/src/components/chart/composite/interactions.ts +247 -0
- package/src/components/chart/composite/price-series.test.ts +39 -0
- package/src/components/chart/composite/price-series.ts +78 -0
- package/src/components/chart/composite/rasterizer.ts +359 -0
- package/src/components/chart/composite/renderers.test.ts +610 -0
- package/src/components/chart/composite/scene.test.ts +464 -0
- package/src/components/chart/composite/scene.ts +553 -0
- package/src/components/chart/composite/text-renderer.ts +285 -0
- package/src/components/chart/composite/time-scale.test.ts +88 -0
- package/src/components/chart/composite/time-scale.ts +217 -0
- package/src/components/chart/composite/types.ts +141 -0
- package/src/components/chart/core/data.ts +81 -5
- package/src/components/chart/core/index.test.ts +128 -43
- package/src/components/chart/core/resolution.ts +0 -6
- package/src/components/chart/native/chart-rasterizer.ts +1 -3
- package/src/components/chart/native/kitty/index.test.ts +99 -120
- package/src/components/chart/native/kitty/manager.ts +3 -0
- package/src/components/chart/native/kitty/protocol.ts +4 -1
- package/src/components/chart/native/raster/crosshair.ts +79 -46
- package/src/components/chart/native/raster/types.ts +2 -11
- package/src/components/chart/native/surface/manager.ts +48 -3
- package/src/components/chart/price-axis-labels.tsx +18 -4
- package/src/components/command-bar/commands/direct/index.ts +25 -0
- package/src/components/command-bar/commands/registry.ts +9 -0
- package/src/components/command-bar/helpers.test.ts +4 -1
- package/src/components/command-bar/list/view.tsx +18 -6
- package/src/components/command-bar/multi-select-picker.tsx +6 -5
- package/src/components/command-bar/pane-settings.test.ts +141 -0
- package/src/components/command-bar/pane-settings.ts +61 -1
- package/src/components/command-bar/panel/body.tsx +7 -6
- package/src/components/command-bar/routes/root/command-items.ts +4 -4
- package/src/components/command-bar/routes/root/results.ts +10 -1
- package/src/components/command-bar/routes/root/selection.test.ts +54 -0
- package/src/components/command-bar/routes/root/selection.ts +13 -0
- package/src/components/command-bar/routing/actions.ts +18 -2
- package/src/components/command-bar/routing/list-state.ts +2 -1
- package/src/components/command-bar/surface/index.test.tsx +36 -4
- package/src/components/command-bar/surface/pane.test.tsx +93 -0
- package/src/components/command-bar/theme-picker.tsx +2 -1
- package/src/components/command-bar/view-model.ts +19 -20
- package/src/components/command-bar/workflow/body.tsx +2 -1
- package/src/components/command-bar/workflow/field-row.tsx +3 -2
- package/src/components/command-bar/workflow/fields.ts +4 -2
- package/src/components/command-bar/workflow/ops.test.ts +161 -0
- package/src/components/command-bar/workflow/ops.ts +20 -50
- package/src/components/command-bar/workflow/route-actions.test.ts +34 -0
- package/src/components/command-bar/workflow/route-actions.ts +4 -0
- package/src/components/command-bar/workflow/types.ts +1 -0
- package/src/components/feed-data-table/stack-view.tsx +6 -5
- package/src/components/index.ts +13 -1
- package/src/components/layout/detached-pane-shell.tsx +2 -1
- package/src/components/layout/floating-pane.tsx +3 -0
- package/src/components/layout/header.tsx +15 -8
- package/src/components/layout/pane/footer/index.test.tsx +22 -3
- package/src/components/layout/pane/footer/index.tsx +3 -1
- package/src/components/layout/pane/footer/model.ts +9 -2
- package/src/components/layout/pane/header.tsx +4 -6
- package/src/components/layout/pane/index.tsx +3 -0
- package/src/components/layout/pane/sidebar.test.tsx +100 -0
- package/src/components/layout/pane/sidebar.tsx +239 -0
- package/src/components/layout/pane/title.ts +4 -3
- package/src/components/layout/shell/action-menu-overlay.tsx +5 -3
- package/src/components/layout/shell/drag/runtime.ts +2 -0
- package/src/components/layout/shell/menu.test.ts +19 -0
- package/src/components/layout/shell/menu.ts +8 -5
- package/src/components/layout/shell/native/pointer-runtime.ts +4 -1
- package/src/components/layout/shell/pane/layers.tsx +2 -2
- package/src/components/layout/status-bar.tsx +8 -7
- package/src/components/layout/window-controls.tsx +4 -3
- package/src/components/metric-treemap/index.tsx +3 -2
- package/src/components/onboarding/onboarding-steps.tsx +30 -29
- package/src/components/onboarding/onboarding-wizard.tsx +5 -4
- package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +15 -12
- package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +17 -16
- package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +6 -5
- package/src/components/onboarding/portfolio-step/choose-panel.tsx +5 -4
- package/src/components/pane-settings-dialog/desktop.tsx +25 -3
- package/src/components/pane-settings-dialog/field-dialogs.tsx +5 -4
- package/src/components/pane-settings-dialog/tui.tsx +11 -9
- package/src/components/pane-settings-dialog/value.ts +8 -5
- package/src/components/pane-settings-dialog.test.tsx +235 -0
- package/src/components/pane-settings-dialog.tsx +24 -0
- package/src/components/pane-template-wizard.tsx +6 -2
- package/src/components/ticker/list-table-view.tsx +3 -2
- package/src/components/ui/button.tsx +3 -1
- package/src/components/ui/choice-dialog.tsx +6 -1
- package/src/components/ui/confirm-dialog.tsx +3 -2
- package/src/components/ui/fields.tsx +10 -1
- package/src/components/ui/frame.tsx +4 -1
- package/src/components/ui/index.ts +5 -0
- package/src/components/ui/inline-quick-add.tsx +104 -0
- package/src/components/ui/list-view.tsx +5 -4
- package/src/components/ui/loading.tsx +2 -1
- package/src/components/ui/multi-select/dialog.tsx +188 -44
- package/src/components/ui/popover.tsx +10 -0
- package/src/components/ui/shortcut-hint.tsx +2 -0
- package/src/components/ui/status.tsx +4 -3
- package/src/components/ui/tabs.tsx +19 -8
- package/src/components/ui/toggle.tsx +44 -3
- package/src/components/ui/ui.test.tsx +67 -5
- package/src/core/app-services.ts +4 -6
- package/src/core/state/session-persistence.ts +3 -2
- package/src/data/config/chart-settings.ts +712 -0
- package/src/data/config/layout.ts +24 -5
- package/src/data/config/store/index.test.ts +377 -14
- package/src/data/config/store/normalize.ts +79 -65
- package/src/data/fred-series.ts +137 -0
- package/src/i18n/index.test.ts +114 -0
- package/src/i18n/index.ts +147 -0
- package/src/i18n/ja.ts +1013 -0
- package/src/i18n/ko.ts +1011 -0
- package/src/i18n/languages.test.ts +31 -0
- package/src/i18n/languages.ts +72 -0
- package/src/i18n/zh-cn.ts +1013 -0
- package/src/i18n/zh-tw.ts +1013 -0
- package/src/plugins/builtin/account-management/footer.ts +3 -2
- package/src/plugins/builtin/account-management/form-components.tsx +3 -2
- package/src/plugins/builtin/account-management/keyboard.ts +6 -0
- package/src/plugins/builtin/account-management/model.ts +25 -21
- package/src/plugins/builtin/account-management/navigation.test.ts +27 -0
- package/src/plugins/builtin/account-management/navigation.ts +24 -0
- package/src/plugins/builtin/account-management/pane.tsx +120 -85
- package/src/plugins/builtin/account-management/password-dialog.tsx +10 -9
- package/src/plugins/builtin/ai/agent-history.ts +139 -0
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +133 -51
- package/src/plugins/builtin/ai/ask-ai.test.tsx +83 -22
- package/src/plugins/builtin/ai/index.test.ts +302 -0
- package/src/plugins/builtin/ai/index.tsx +374 -40
- package/src/plugins/builtin/ai/pane-settings.test.ts +155 -0
- package/src/plugins/builtin/ai/pane-settings.ts +319 -0
- package/src/plugins/builtin/ai/pi/credential-store.test.ts +80 -0
- package/src/plugins/builtin/ai/pi/credential-store.ts +138 -0
- package/src/plugins/builtin/ai/pi/file-store.ts +138 -0
- package/src/plugins/builtin/ai/pi/host.test.ts +500 -0
- package/src/plugins/builtin/ai/pi/host.ts +564 -0
- package/src/plugins/builtin/ai/pi/index.ts +5 -0
- package/src/plugins/builtin/ai/pi/models-store.test.ts +44 -0
- package/src/plugins/builtin/ai/pi/models-store.ts +141 -0
- package/src/plugins/builtin/ai/pi/providers.ts +68 -0
- package/src/plugins/builtin/ai/pi/runtime.test.ts +240 -0
- package/src/plugins/builtin/ai/pi/runtime.ts +635 -0
- package/src/plugins/builtin/ai/providers.test.ts +68 -0
- package/src/plugins/builtin/ai/providers.ts +163 -38
- package/src/plugins/builtin/ai/runner-selection.test.ts +174 -0
- package/src/plugins/builtin/ai/runner-selection.ts +193 -0
- package/src/plugins/builtin/ai/runner-selector.tsx +153 -0
- package/src/plugins/builtin/ai/runner.test.ts +198 -0
- package/src/plugins/builtin/ai/runner.ts +276 -73
- package/src/plugins/builtin/ai/screener/contract.test.ts +26 -73
- package/src/plugins/builtin/ai/screener/contract.ts +14 -52
- package/src/plugins/builtin/ai/screener/editor-runtime.ts +55 -5
- package/src/plugins/builtin/ai/screener/editor.tsx +42 -36
- package/src/plugins/builtin/ai/screener/footer.ts +82 -67
- package/src/plugins/builtin/ai/screener/keyboard.ts +27 -11
- package/src/plugins/builtin/ai/screener/model.test.ts +24 -0
- package/src/plugins/builtin/ai/screener/model.ts +9 -3
- package/src/plugins/builtin/ai/screener/pane.test.tsx +142 -55
- package/src/plugins/builtin/ai/screener/pane.tsx +95 -97
- package/src/plugins/builtin/ai/screener/results-view.tsx +8 -41
- package/src/plugins/builtin/ai/screener/runner.ts +41 -25
- package/src/plugins/builtin/ai/screener/tabs-bar.tsx +2 -2
- package/src/plugins/builtin/ai/use-runtime-providers.ts +28 -0
- package/src/plugins/builtin/ai/workspace/model.test.ts +227 -0
- package/src/plugins/builtin/ai/workspace/model.ts +308 -0
- package/src/plugins/builtin/ai/workspace/pane.test.tsx +197 -0
- package/src/plugins/builtin/ai/workspace/pane.tsx +896 -0
- package/src/plugins/builtin/analytics/index.test.tsx +3 -3
- package/src/plugins/builtin/analytics/index.tsx +3 -8
- package/src/plugins/builtin/broker-manager/detail.tsx +25 -24
- package/src/plugins/builtin/broker-manager/index.test.tsx +1 -1
- package/src/plugins/builtin/broker-manager/index.tsx +9 -11
- package/src/plugins/builtin/broker-manager/model.ts +11 -10
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +22 -21
- package/src/plugins/builtin/broker-manager/table.ts +9 -8
- package/src/plugins/builtin/changelog/index.tsx +3 -8
- package/src/plugins/builtin/chart-composer/chart-spec.ts +89 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +98 -0
- package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +120 -0
- package/src/plugins/builtin/chart-composer/editor.test.tsx +332 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +1107 -0
- package/src/plugins/builtin/chart-composer/index.tsx +228 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +645 -0
- package/src/plugins/builtin/chart-composer/presets.test.ts +561 -0
- package/src/plugins/builtin/chart-composer/presets.ts +759 -0
- package/src/plugins/builtin/chart-composer/quick-add.test.tsx +245 -0
- package/src/plugins/builtin/chart-composer/quick-add.tsx +409 -0
- package/src/plugins/builtin/chart-composer/semantic.ts +86 -0
- package/src/plugins/builtin/chart-composer/series-catalog.test.ts +65 -0
- package/src/plugins/builtin/chart-composer/series-catalog.ts +295 -0
- package/src/plugins/builtin/chart-composer/settings.test.ts +163 -0
- package/src/plugins/builtin/chart-composer/settings.ts +397 -0
- package/src/plugins/builtin/chart-composer/shortcuts.test.ts +44 -0
- package/src/plugins/builtin/chart-composer/shortcuts.ts +35 -0
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +103 -0
- package/src/plugins/builtin/chat/channel-labels.ts +5 -4
- package/src/plugins/builtin/chat/channels.ts +12 -11
- package/src/plugins/builtin/chat/chat.test.tsx +32 -0
- package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
- package/src/plugins/builtin/chat/content/composer.tsx +9 -8
- package/src/plugins/builtin/chat/content/index.tsx +36 -1
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +8 -7
- package/src/plugins/builtin/chat/content/transcript.tsx +9 -3
- package/src/plugins/builtin/chat/controller/utils.ts +10 -9
- package/src/plugins/builtin/chat/message/desktop.tsx +17 -10
- package/src/plugins/builtin/chat/message/profile-popover.test.tsx +122 -3
- package/src/plugins/builtin/chat/message/profile-popover.tsx +38 -1
- package/src/plugins/builtin/chat/message/terminal.tsx +19 -10
- package/src/plugins/builtin/chat/pane-state.test.ts +31 -0
- package/src/plugins/builtin/chat/pane-state.ts +30 -0
- package/src/plugins/builtin/chat/pane.tsx +27 -4
- package/src/plugins/builtin/chat/profile-popover.ts +62 -5
- package/src/plugins/builtin/chat/sidebar.test.tsx +4 -7
- package/src/plugins/builtin/chat/sidebar.tsx +107 -168
- package/src/plugins/builtin/chat/test-harness.tsx +6 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +5 -4
- package/src/plugins/builtin/cloud/plugin.tsx +150 -127
- package/src/plugins/builtin/composite-plugins.ts +71 -0
- package/src/plugins/builtin/correlation/index.test.tsx +4 -4
- package/src/plugins/builtin/correlation/index.tsx +3 -8
- package/src/plugins/builtin/earnings/index.tsx +37 -21
- package/src/plugins/builtin/earnings/model.test.ts +52 -0
- package/src/plugins/builtin/earnings/model.ts +32 -0
- package/src/plugins/builtin/econ/detail-view.tsx +17 -4
- package/src/plugins/builtin/econ/fred-cache.test.ts +57 -11
- package/src/plugins/builtin/econ/index.tsx +16 -20
- package/src/plugins/builtin/fear-greed/index.tsx +2 -8
- package/src/plugins/builtin/fx-matrix/index.tsx +3 -8
- package/src/plugins/builtin/help/components.tsx +5 -4
- package/src/plugins/builtin/help/index.tsx +14 -17
- package/src/plugins/builtin/holders/index.tsx +2 -8
- package/src/plugins/builtin/insider/index.tsx +2 -7
- package/src/plugins/builtin/kelly-sizer/index.test.tsx +2 -2
- package/src/plugins/builtin/kelly-sizer/index.tsx +3 -7
- package/src/plugins/builtin/kelly-sizer/test-support.tsx +3 -3
- package/src/plugins/builtin/layout-manager/index.tsx +3 -7
- package/src/plugins/builtin/market-heatmap/index.tsx +3 -8
- package/src/plugins/builtin/market-movers/index.tsx +3 -8
- package/src/plugins/builtin/news/index.tsx +12 -16
- package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +3 -3
- package/src/plugins/builtin/news/wire/index.tsx +83 -73
- package/src/plugins/builtin/options/index.tsx +2 -8
- package/src/plugins/builtin/plugin-module.test.ts +89 -0
- package/src/plugins/builtin/plugin-module.ts +116 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +4 -3
- package/src/plugins/builtin/portfolio-list/index.test.ts +17 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +3 -5
- package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +3 -3
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +53 -53
- package/src/plugins/builtin/portfolio-list/settings.ts +5 -3
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +4 -3
- package/src/plugins/builtin/research/index.tsx +2 -8
- package/src/plugins/builtin/sec/index.tsx +2 -8
- package/src/plugins/builtin/sectors/index.tsx +3 -8
- package/src/plugins/builtin/thirteenf/index.tsx +3 -8
- package/src/plugins/builtin/thirteenf/pane.test.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/chart-tab-switch.test.tsx +49 -23
- package/src/plugins/builtin/ticker-detail/index.test.tsx +15 -136
- package/src/plugins/builtin/ticker-detail/index.tsx +2 -93
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +6 -5
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +41 -17
- package/src/plugins/builtin/ticker-detail/pane.tsx +4 -3
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.test.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +3 -50
- package/src/plugins/builtin/ticker-research-backend-plugin.ts +17 -0
- package/src/plugins/builtin/ticker-research-plugin.ts +27 -0
- package/src/plugins/builtin/tv/channels.ts +27 -0
- package/src/plugins/builtin/tv/index.tsx +40 -0
- package/src/plugins/builtin/tv/pane.tsx +286 -0
- package/src/plugins/builtin/tv/youtube-stream.ts +100 -0
- package/src/plugins/builtin/world-indices/index.tsx +3 -8
- package/src/plugins/builtin/yield-curve/index.tsx +8 -8
- package/src/plugins/catalog-backend.test.ts +21 -0
- package/src/plugins/catalog-backend.ts +42 -0
- package/src/plugins/catalog-ui.ts +6 -14
- package/src/plugins/ibkr/instance-selection.test.ts +0 -1
- package/src/plugins/ownership.ts +143 -0
- package/src/plugins/prediction-markets/backend-plugin.ts +19 -0
- package/src/plugins/prediction-markets/chart.test.tsx +7 -5
- package/src/plugins/prediction-markets/chart.tsx +25 -7
- package/src/plugins/registry/index.test.ts +51 -0
- package/src/plugins/registry/index.ts +8 -0
- package/src/plugins/registry/pane-settings.ts +1 -1
- package/src/plugins/runtime/index.test.tsx +2 -2
- package/src/remote/app-host.tsx +16 -3
- package/src/remote/controller.test.ts +102 -1
- package/src/remote/controller.ts +122 -0
- package/src/remote/types.ts +11 -0
- package/src/renderers/electrobun/bun/core-capabilities.ts +123 -27
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/media.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/window-style.ts +4 -1
- package/src/renderers/electrobun/bun/desktop/workspace/index.test.ts +32 -1
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +10 -0
- package/src/renderers/electrobun/bun/index.ts +5 -0
- package/src/renderers/electrobun/bun/tui-entry.ts +1 -6
- package/src/renderers/electrobun/view/ai-host.ts +124 -19
- package/src/renderers/electrobun/view/backend-rpc.ts +4 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +151 -39
- package/src/renderers/electrobun/view/desktop/popover.tsx +166 -0
- package/src/renderers/electrobun/view/dialog-host.test.ts +25 -0
- package/src/renderers/electrobun/view/dialog-host.tsx +75 -12
- package/src/renderers/electrobun/view/host/box.tsx +5 -0
- package/src/renderers/electrobun/view/host/chart-surface.tsx +10 -9
- package/src/renderers/electrobun/view/host/input.tsx +9 -19
- package/src/renderers/electrobun/view/host/media-surface.tsx +170 -0
- package/src/renderers/electrobun/view/host/mouse.ts +1 -0
- package/src/renderers/electrobun/view/host/style.ts +1 -1
- package/src/renderers/electrobun/view/host/tabs.tsx +4 -3
- package/src/renderers/electrobun/view/main.tsx +10 -3
- package/src/renderers/electrobun/view/styles.css +17 -0
- package/src/renderers/electrobun/view/ui-host.tsx +7 -0
- package/src/renderers/opentui/chart-surface.test.tsx +52 -6
- package/src/renderers/opentui/chart-surface.tsx +95 -11
- package/src/renderers/opentui/dialog-host.tsx +36 -5
- package/src/renderers/opentui/host.tsx +39 -0
- package/src/renderers/opentui/start.tsx +30 -0
- package/src/renderers/opentui/ui-host.tsx +2 -0
- package/src/sources/provider-router/index.test.ts +43 -0
- package/src/sources/provider-router/index.ts +4 -0
- package/src/sources/provider-router/supplemental.ts +46 -0
- package/src/sources/sec-edgar.test.ts +203 -2
- package/src/sources/sec-edgar.ts +70 -15
- package/src/state/app/context/index.tsx +13 -1
- package/src/state/app/context/selector.test.tsx +62 -1
- package/src/state/session-persistence.test.ts +13 -2
- package/src/sync/controller.test.ts +190 -3
- package/src/sync/controller.ts +76 -45
- package/src/sync/core-contributors.test.ts +121 -0
- package/src/sync/core-contributors.ts +104 -27
- package/src/sync/react.ts +6 -8
- package/src/sync/types.ts +3 -0
- package/src/time-series/alignment.ts +158 -0
- package/src/time-series/economic.ts +49 -0
- package/src/time-series/field-catalog.ts +306 -0
- package/src/time-series/fundamentals.test.ts +505 -0
- package/src/time-series/fundamentals.ts +842 -0
- package/src/time-series/hooks.ts +45 -0
- package/src/time-series/index.ts +14 -0
- package/src/time-series/live-quotes.test.ts +219 -0
- package/src/time-series/live-quotes.ts +203 -0
- package/src/time-series/market-resolution.ts +53 -0
- package/src/time-series/market.ts +125 -0
- package/src/time-series/reporting.ts +98 -0
- package/src/time-series/resolve.test.ts +1281 -0
- package/src/time-series/resolve.ts +783 -0
- package/src/time-series/spec-studies.test.ts +400 -0
- package/src/time-series/spec.ts +487 -0
- package/src/time-series/studies.ts +563 -0
- package/src/time-series/transforms.test.ts +105 -0
- package/src/time-series/transforms.ts +152 -0
- package/src/time-series/types.ts +162 -0
- package/src/time-series/use-chart-resolution.ts +166 -0
- package/src/types/config.ts +9 -11
- package/src/types/desktop-window.ts +1 -0
- package/src/types/financials.ts +4 -0
- package/src/types/media.ts +17 -0
- package/src/types/plugin.ts +32 -1
- package/src/ui/context-menu.tsx +15 -3
- package/src/ui/dialog.tsx +14 -2
- package/src/ui/host.tsx +40 -1
- package/src/ui/index.tsx +9 -0
- package/src/utils/exchanges.test.ts +13 -1
- package/src/utils/exchanges.ts +10 -0
- package/src/utils/financial-statements.test.ts +138 -0
- package/src/utils/financial-statements.ts +115 -8
- package/src/utils/format.test.ts +11 -1
- package/src/utils/format.ts +7 -0
- package/src/version.ts +1 -1
- package/src/components/chart/bar-chart-renderer.test.ts +0 -233
- package/src/components/chart/bar-chart-renderer.ts +0 -445
- package/src/components/chart/comparison/data.test.ts +0 -159
- package/src/components/chart/comparison/data.ts +0 -302
- package/src/components/chart/comparison/renderer.test.ts +0 -187
- package/src/components/chart/comparison/renderer.ts +0 -282
- package/src/components/chart/comparison/scene.ts +0 -178
- package/src/components/chart/comparison-stock-chart/axis-width.ts +0 -99
- package/src/components/chart/comparison-stock-chart/canvas.ts +0 -148
- package/src/components/chart/comparison-stock-chart/controls.ts +0 -252
- package/src/components/chart/comparison-stock-chart/helpers.ts +0 -101
- package/src/components/chart/comparison-stock-chart/interactions.ts +0 -208
- package/src/components/chart/comparison-stock-chart/layout-metrics.ts +0 -62
- package/src/components/chart/comparison-stock-chart/layout.tsx +0 -155
- package/src/components/chart/comparison-stock-chart/legend.tsx +0 -74
- package/src/components/chart/comparison-stock-chart/native-surfaces.ts +0 -89
- package/src/components/chart/comparison-stock-chart/render-data.ts +0 -227
- package/src/components/chart/comparison-stock-chart/render-output.ts +0 -140
- package/src/components/chart/comparison-stock-chart/selection-runtime.ts +0 -128
- package/src/components/chart/comparison-stock-chart/sources.ts +0 -51
- package/src/components/chart/comparison-stock-chart/surface-runtime.ts +0 -159
- package/src/components/chart/comparison-stock-chart/toolbar.tsx +0 -117
- package/src/components/chart/comparison-stock-chart/types.ts +0 -22
- package/src/components/chart/comparison-stock-chart/view.tsx +0 -572
- package/src/components/chart/comparison-stock-chart/viewport-runtime.ts +0 -239
- package/src/components/chart/comparison-stock-chart.tsx +0 -24
- package/src/components/chart/core/axis-measure.ts +0 -33
- package/src/components/chart/core/controller.test.ts +0 -157
- package/src/components/chart/core/controller.ts +0 -349
- package/src/components/chart/core/indicator-selector.tsx +0 -75
- package/src/components/chart/core/keyboard.ts +0 -24
- package/src/components/chart/core/pane-settings.test.ts +0 -52
- package/src/components/chart/core/pane-settings.ts +0 -141
- package/src/components/chart/core/scroll.test.ts +0 -64
- package/src/components/chart/core/scroll.ts +0 -82
- package/src/components/chart/core/window-viewport.test.ts +0 -69
- package/src/components/chart/core/window-viewport.ts +0 -162
- package/src/components/chart/display-cursor.ts +0 -153
- package/src/components/chart/indicators/bands.test.ts +0 -36
- package/src/components/chart/indicators/bands.ts +0 -29
- package/src/components/chart/indicators/moving-averages.test.ts +0 -39
- package/src/components/chart/indicators/moving-averages.ts +0 -42
- package/src/components/chart/indicators/options.test.ts +0 -32
- package/src/components/chart/indicators/options.ts +0 -107
- package/src/components/chart/indicators/oscillators.test.ts +0 -58
- package/src/components/chart/indicators/oscillators.ts +0 -92
- package/src/components/chart/native/canvas-bitmaps.ts +0 -140
- package/src/components/chart/native/raster/comparison-chart.ts +0 -162
- package/src/components/chart/native/surface/hooks.ts +0 -144
- package/src/components/chart/native/surface/rendering.ts +0 -211
- package/src/components/chart/static/bar-chart-surface.test.tsx +0 -101
- package/src/components/chart/static/bar-chart-surface.tsx +0 -292
- package/src/components/chart/stock-chart/auto.ts +0 -216
- package/src/components/chart/stock-chart/axis-width.ts +0 -146
- package/src/components/chart/stock-chart/bitmaps.ts +0 -22
- package/src/components/chart/stock-chart/canvas.ts +0 -202
- package/src/components/chart/stock-chart/controls.tsx +0 -149
- package/src/components/chart/stock-chart/cursor.ts +0 -60
- package/src/components/chart/stock-chart/data-runtime.ts +0 -128
- package/src/components/chart/stock-chart/footer.test.ts +0 -65
- package/src/components/chart/stock-chart/footer.ts +0 -303
- package/src/components/chart/stock-chart/geometry.ts +0 -160
- package/src/components/chart/stock-chart/index.test.ts +0 -532
- package/src/components/chart/stock-chart/index.tsx +0 -29
- package/src/components/chart/stock-chart/indicators.ts +0 -226
- package/src/components/chart/stock-chart/interaction-runtime.ts +0 -44
- package/src/components/chart/stock-chart/interactions.ts +0 -300
- package/src/components/chart/stock-chart/keyboard.ts +0 -373
- package/src/components/chart/stock-chart/layout-metrics.ts +0 -62
- package/src/components/chart/stock-chart/layout.tsx +0 -177
- package/src/components/chart/stock-chart/native-surfaces.ts +0 -121
- package/src/components/chart/stock-chart/presentation.ts +0 -67
- package/src/components/chart/stock-chart/renderer-switch.test.tsx +0 -263
- package/src/components/chart/stock-chart/rendering/bounds.ts +0 -116
- package/src/components/chart/stock-chart/rendering/candidates.ts +0 -145
- package/src/components/chart/stock-chart/rendering/data.ts +0 -370
- package/src/components/chart/stock-chart/rendering/invalidation.ts +0 -68
- package/src/components/chart/stock-chart/rendering/output.ts +0 -304
- package/src/components/chart/stock-chart/rendering/planning.ts +0 -122
- package/src/components/chart/stock-chart/rendering/projection.test.tsx +0 -98
- package/src/components/chart/stock-chart/rendering/projection.ts +0 -112
- package/src/components/chart/stock-chart/rendering/state.test.ts +0 -70
- package/src/components/chart/stock-chart/rendering/state.ts +0 -283
- package/src/components/chart/stock-chart/requests.ts +0 -337
- package/src/components/chart/stock-chart/resolution-support.ts +0 -96
- package/src/components/chart/stock-chart/resolved/runtime.ts +0 -289
- package/src/components/chart/stock-chart/resolved.tsx +0 -359
- package/src/components/chart/stock-chart/settings.ts +0 -81
- package/src/components/chart/stock-chart/surface-runtime.ts +0 -62
- package/src/components/chart/stock-chart/types.ts +0 -25
- package/src/components/chart/stock-chart/view.tsx +0 -155
- package/src/components/chart/stock-chart/viewport/effects.ts +0 -293
- package/src/components/chart/stock-chart/viewport/index.ts +0 -144
- package/src/components/chart/stock-chart/viewport/runtime.ts +0 -375
- package/src/components/chart/time-axis-label.tsx +0 -175
- package/src/plugins/builtin/ai/gloomberb-cli.ts +0 -71
- package/src/plugins/builtin/ai/screener/action-bar.tsx +0 -58
- package/src/plugins/builtin/comparison-chart/index.test.tsx +0 -848
- package/src/plugins/builtin/comparison-chart/index.tsx +0 -194
- package/src/plugins/builtin/econ/fred-cache.ts +0 -106
- package/src/plugins/builtin/plugin-groups.ts +0 -130
- package/src/plugins/builtin/ticker-detail/chart-pane.tsx +0 -91
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -100
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/content.tsx +0 -296
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/data.ts +0 -62
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/model.ts +0 -608
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/settings.ts +0 -65
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph/types.ts +0 -58
- package/src/plugins/builtin/ticker-detail/data-panes/fundamental-graph.tsx +0 -138
- package/src/plugins/builtin/ticker-detail/data-panes.test.ts +0 -220
|
@@ -647,6 +647,38 @@ describe("ChatContent", () => {
|
|
|
647
647
|
expect(bodySpan!.fg.toInts().join(",")).toBe(expectedSelectedFg);
|
|
648
648
|
});
|
|
649
649
|
|
|
650
|
+
test("selects the exact message requested by a chat deep link", async () => {
|
|
651
|
+
const controller = createController({
|
|
652
|
+
messages: [makeMessage(1), makeMessage(2)],
|
|
653
|
+
});
|
|
654
|
+
let handled = 0;
|
|
655
|
+
|
|
656
|
+
await act(async () => {
|
|
657
|
+
testSetup = await testRender(createHarness(controller, {
|
|
658
|
+
width: 72,
|
|
659
|
+
height: 12,
|
|
660
|
+
targetMessageId: "m1",
|
|
661
|
+
onTargetMessageHandled: () => {
|
|
662
|
+
handled += 1;
|
|
663
|
+
},
|
|
664
|
+
}), {
|
|
665
|
+
width: 72,
|
|
666
|
+
height: 12,
|
|
667
|
+
});
|
|
668
|
+
});
|
|
669
|
+
|
|
670
|
+
await flushFrame();
|
|
671
|
+
|
|
672
|
+
const expectedSelectedFg = hexToRgbaInts(colors.selectedText);
|
|
673
|
+
const frame = setup().captureSpans();
|
|
674
|
+
const bodyLine = frame.lines.find((line) => lineText(line).includes("message 1"));
|
|
675
|
+
const bodySpan = bodyLine?.spans.find((span) => span.text.includes("message 1"));
|
|
676
|
+
|
|
677
|
+
expect(handled).toBe(1);
|
|
678
|
+
expect(bodySpan).toBeDefined();
|
|
679
|
+
expect(bodySpan!.fg.toInts().join(",")).toBe(expectedSelectedFg);
|
|
680
|
+
});
|
|
681
|
+
|
|
650
682
|
test("grows the composer for multi-line drafts", async () => {
|
|
651
683
|
const controller = createController();
|
|
652
684
|
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
formatInlinePreview,
|
|
7
7
|
} from "../layout";
|
|
8
8
|
import { parseChatComposerCommand } from "../composer-commands";
|
|
9
|
+
import { t, tf } from "../../../../i18n";
|
|
9
10
|
import type { ChatContentController } from "./types";
|
|
10
11
|
import { getComposerCursorOffset, moveComposerCursorToOffset } from "./composer-cursor";
|
|
11
12
|
|
|
@@ -360,7 +361,11 @@ export function useChatComposerRuntime({
|
|
|
360
361
|
Math.max(contentWidth - " editing: ".length - COMPOSER_ACTION_WIDTH - 1, 0),
|
|
361
362
|
)
|
|
362
363
|
: "";
|
|
363
|
-
const inputPlaceholder = editingMessage
|
|
364
|
+
const inputPlaceholder = editingMessage
|
|
365
|
+
? t("Edit message...")
|
|
366
|
+
: replyTo
|
|
367
|
+
? tf("Reply to @{username}...", { username: replyTo.user.username })
|
|
368
|
+
: t("Type a message...");
|
|
364
369
|
|
|
365
370
|
return {
|
|
366
371
|
beginEditLatestMessage,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Box, Text, TextAttributes, type TextareaRenderable } from "../../../../ui";
|
|
2
2
|
import { getMessageComposerBlockHeight, MessageComposer } from "../../../../components/ui";
|
|
3
3
|
import { colors } from "../../../../theme/colors";
|
|
4
|
+
import { t } from "../../../../i18n";
|
|
4
5
|
import type { ChatMessage } from "../../../../api-client";
|
|
5
6
|
import { InlineAuthActions } from "../../cloud/auth-actions";
|
|
6
7
|
import { ChatActionChip } from "../message/action-chip";
|
|
@@ -133,18 +134,18 @@ export function ChatComposerArea({
|
|
|
133
134
|
<Box width={contentWidth} height={2} flexDirection="column">
|
|
134
135
|
{!user && !hasSavedSession ? (
|
|
135
136
|
<>
|
|
136
|
-
<Text fg={colors.textDim}>Read-only chat. Log in or sign up to send
|
|
137
|
+
<Text fg={colors.textDim}>{t("Read-only chat. Log in or sign up to send.")}</Text>
|
|
137
138
|
<InlineAuthActions />
|
|
138
139
|
</>
|
|
139
140
|
) : !user ? (
|
|
140
141
|
<>
|
|
141
|
-
<Text fg={colors.positive}>Saved login found. Log in again to send
|
|
142
|
+
<Text fg={colors.positive}>{t("Saved login found. Log in again to send.")}</Text>
|
|
142
143
|
<InlineAuthActions showSignup={false} />
|
|
143
144
|
</>
|
|
144
145
|
) : (
|
|
145
146
|
<>
|
|
146
|
-
<Text fg={colors.positive}>Verify your email to send messages
|
|
147
|
-
<Text fg={colors.textDim}>Ctrl+P, then Resend Verification Email</Text>
|
|
147
|
+
<Text fg={colors.positive}>{t("Verify your email to send messages.")}</Text>
|
|
148
|
+
<Text fg={colors.textDim}>{t("Ctrl+P, then Resend Verification Email")}</Text>
|
|
148
149
|
</>
|
|
149
150
|
)}
|
|
150
151
|
</Box>
|
|
@@ -166,11 +167,11 @@ export function ChatComposerArea({
|
|
|
166
167
|
|
|
167
168
|
{editingMessage && (
|
|
168
169
|
<Box height={1} width={contentWidth} flexDirection="row">
|
|
169
|
-
<Text fg={colors.textMuted}> editing </Text>
|
|
170
|
+
<Text fg={colors.textMuted}>{` ${t("editing")} `}</Text>
|
|
170
171
|
<Text fg={colors.textDim}>{editingPreview ? `: ${editingPreview}` : ""}</Text>
|
|
171
172
|
<Box flexGrow={1} />
|
|
172
173
|
<ChatActionChip
|
|
173
|
-
label="Cancel"
|
|
174
|
+
label={t("Cancel")}
|
|
174
175
|
width={COMPOSER_ACTION_WIDTH}
|
|
175
176
|
onPress={cancelEditMessage}
|
|
176
177
|
/>
|
|
@@ -179,12 +180,12 @@ export function ChatComposerArea({
|
|
|
179
180
|
|
|
180
181
|
{!editingMessage && replyTo && (
|
|
181
182
|
<Box height={1} width={contentWidth} flexDirection="row">
|
|
182
|
-
<Text fg={colors.textMuted}> replying to </Text>
|
|
183
|
+
<Text fg={colors.textMuted}>{` ${t("replying to")} `}</Text>
|
|
183
184
|
<Text fg={colors.positive} attributes={TextAttributes.BOLD}>{`@${replyTo.user.username}`}</Text>
|
|
184
185
|
<Text fg={colors.textDim}>{replyPreview ? `: ${replyPreview}` : ""}</Text>
|
|
185
186
|
<Box flexGrow={1} />
|
|
186
187
|
<ChatActionChip
|
|
187
|
-
label="Cancel"
|
|
188
|
+
label={t("Cancel")}
|
|
188
189
|
width={COMPOSER_ACTION_WIDTH}
|
|
189
190
|
onPress={clearReplyTarget}
|
|
190
191
|
/>
|
|
@@ -33,6 +33,8 @@ import { useChatComposerRuntime } from "./composer-runtime";
|
|
|
33
33
|
import { useChatMessageSelection } from "./selection-runtime";
|
|
34
34
|
import type { ChatMessage } from "../../../../api-client";
|
|
35
35
|
import { NewDmDialog } from "./new-dm-dialog";
|
|
36
|
+
import { usePluginAppActions } from "../../../runtime";
|
|
37
|
+
import { requestAccountManagementTab } from "../../account-management/navigation";
|
|
36
38
|
import {
|
|
37
39
|
CHAT_MESSAGE_EDIT_WINDOW_MS,
|
|
38
40
|
findLatestEditableChatMessage,
|
|
@@ -53,6 +55,8 @@ interface ChatContentProps {
|
|
|
53
55
|
onChannelChange?: (channelId: string) => void;
|
|
54
56
|
onChannelTitleChange?: (title: string) => void;
|
|
55
57
|
controller?: ChatContentController;
|
|
58
|
+
targetMessageId?: string;
|
|
59
|
+
onTargetMessageHandled?: () => void;
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
export function ChatContent({
|
|
@@ -63,8 +67,11 @@ export function ChatContent({
|
|
|
63
67
|
onChannelChange,
|
|
64
68
|
onChannelTitleChange,
|
|
65
69
|
controller = chatController,
|
|
70
|
+
targetMessageId,
|
|
71
|
+
onTargetMessageHandled,
|
|
66
72
|
}: ChatContentProps) {
|
|
67
73
|
const dispatch = useAppDispatch();
|
|
74
|
+
const { showPane } = usePluginAppActions();
|
|
68
75
|
const commandBarOpen = useAppSelector((state) => state.commandBarOpen);
|
|
69
76
|
const channelId = normalizeChannelId(rawChannelId);
|
|
70
77
|
const channelIdRef = useRef(channelId);
|
|
@@ -233,6 +240,16 @@ export function ChatContent({
|
|
|
233
240
|
showProfilePopover,
|
|
234
241
|
} = useChatProfilePopover();
|
|
235
242
|
|
|
243
|
+
const showUserProfilePopover = useCallback((targetUser: Parameters<typeof showProfilePopover>[0]) => {
|
|
244
|
+
showProfilePopover(targetUser, { ownProfile: targetUser.id === user?.id });
|
|
245
|
+
}, [showProfilePopover, user?.id]);
|
|
246
|
+
|
|
247
|
+
const openProfileSetup = useCallback(() => {
|
|
248
|
+
closeProfilePopover();
|
|
249
|
+
requestAccountManagementTab("profile");
|
|
250
|
+
showPane("account-management");
|
|
251
|
+
}, [closeProfilePopover, showPane]);
|
|
252
|
+
|
|
236
253
|
const blurInput = useCallback(() => {
|
|
237
254
|
setInputFocused(false);
|
|
238
255
|
dispatch({ type: "SET_INPUT_CAPTURED", captured: false });
|
|
@@ -432,6 +449,23 @@ export function ChatContent({
|
|
|
432
449
|
useDefaultControllerChannel,
|
|
433
450
|
});
|
|
434
451
|
|
|
452
|
+
const handledTargetMessageRef = useRef<string | null>(null);
|
|
453
|
+
useEffect(() => {
|
|
454
|
+
if (!targetMessageId || loading || messages.length === 0) return;
|
|
455
|
+
const targetKey = `${channelId}:${targetMessageId}`;
|
|
456
|
+
if (handledTargetMessageRef.current === targetKey) return;
|
|
457
|
+
handledTargetMessageRef.current = targetKey;
|
|
458
|
+
jumpToMessage(targetMessageId);
|
|
459
|
+
onTargetMessageHandled?.();
|
|
460
|
+
}, [
|
|
461
|
+
channelId,
|
|
462
|
+
jumpToMessage,
|
|
463
|
+
loading,
|
|
464
|
+
messages.length,
|
|
465
|
+
onTargetMessageHandled,
|
|
466
|
+
targetMessageId,
|
|
467
|
+
]);
|
|
468
|
+
|
|
435
469
|
useChatContentShortcuts({
|
|
436
470
|
beginEditLatestMessage,
|
|
437
471
|
beginReplyTo,
|
|
@@ -550,10 +584,11 @@ export function ChatContent({
|
|
|
550
584
|
scrollRef={scrollRef}
|
|
551
585
|
selectedIdx={selectedIdx}
|
|
552
586
|
setHoveredIdx={setHoveredIdx}
|
|
553
|
-
showProfilePopover={
|
|
587
|
+
showProfilePopover={showUserProfilePopover}
|
|
554
588
|
stickyTranscript={stickyTranscript}
|
|
555
589
|
user={user}
|
|
556
590
|
userByUsername={userByUsername}
|
|
591
|
+
onSetUpProfile={openProfileSetup}
|
|
557
592
|
/>
|
|
558
593
|
|
|
559
594
|
{newDmOpen ? (
|
|
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|
|
2
2
|
import { ListView, TextField, type ListViewItem } from "../../../../components/ui";
|
|
3
3
|
import { useShortcut } from "../../../../react/input";
|
|
4
4
|
import { colors, hoverBg } from "../../../../theme/colors";
|
|
5
|
+
import { t } from "../../../../i18n";
|
|
5
6
|
import { Box, Text, TextAttributes, type InputRenderable } from "../../../../ui";
|
|
6
7
|
import type { ChatUserSummary } from "../../../../api-client";
|
|
7
8
|
import { isPlainKey } from "../../../../utils/keyboard";
|
|
@@ -124,7 +125,7 @@ export function NewDmDialog({
|
|
|
124
125
|
const submittedValue = valueRef.current;
|
|
125
126
|
const submittedUsernames = parseDmUsernames(submittedValue);
|
|
126
127
|
if (!hasOnlyDmUsernameArgs(submittedValue) || submittedUsernames.length === 0 || submitting) {
|
|
127
|
-
setError("Enter at least one @username.");
|
|
128
|
+
setError(t("Enter at least one @username."));
|
|
128
129
|
return;
|
|
129
130
|
}
|
|
130
131
|
setSubmitting(true);
|
|
@@ -132,7 +133,7 @@ export function NewDmDialog({
|
|
|
132
133
|
try {
|
|
133
134
|
await onSubmit(submittedUsernames);
|
|
134
135
|
} catch {
|
|
135
|
-
setError("Could not start conversation.");
|
|
136
|
+
setError(t("Could not start conversation."));
|
|
136
137
|
setSubmitting(false);
|
|
137
138
|
}
|
|
138
139
|
};
|
|
@@ -187,7 +188,7 @@ export function NewDmDialog({
|
|
|
187
188
|
style={{ zIndex: 8 }}
|
|
188
189
|
>
|
|
189
190
|
<Box height={1} flexDirection="row">
|
|
190
|
-
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>New DM</Text>
|
|
191
|
+
<Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{t("New DM")}</Text>
|
|
191
192
|
<Box flexGrow={1} />
|
|
192
193
|
<Box width={3} height={1} backgroundColor={colors.panel} onMouseDown={onCancel} style={{ cursor: "pointer" }}>
|
|
193
194
|
<Text fg={colors.text}> x </Text>
|
|
@@ -208,7 +209,7 @@ export function NewDmDialog({
|
|
|
208
209
|
/>
|
|
209
210
|
<Box height={1}>
|
|
210
211
|
<Text fg={selectedUsernames.length > 0 ? colors.textMuted : colors.textDim}>
|
|
211
|
-
{selectedUsernames.length > 0 ? usernamesLabel(selectedUsernames, contentWidth) : "Recent users"}
|
|
212
|
+
{selectedUsernames.length > 0 ? usernamesLabel(selectedUsernames, contentWidth) : t("Recent users")}
|
|
212
213
|
</Text>
|
|
213
214
|
</Box>
|
|
214
215
|
<ListView
|
|
@@ -218,7 +219,7 @@ export function NewDmDialog({
|
|
|
218
219
|
bgColor={colors.bg}
|
|
219
220
|
selectedBgColor={colors.selected}
|
|
220
221
|
hoverBgColor={hoverBg()}
|
|
221
|
-
emptyMessage="No recent users"
|
|
222
|
+
emptyMessage={t("No recent users")}
|
|
222
223
|
selectOnHover
|
|
223
224
|
onSelect={setSelectedIndex}
|
|
224
225
|
onActivate={(item) => toggleCandidate(item.id)}
|
|
@@ -244,7 +245,7 @@ export function NewDmDialog({
|
|
|
244
245
|
{error ? (
|
|
245
246
|
<Text fg={colors.negative}>{truncateChannelLabel(error, contentWidth)}</Text>
|
|
246
247
|
) : (
|
|
247
|
-
<Text fg={colors.textDim}>{selectedUsernames.length > 1 ? "Group chat" : "Direct message"}</Text>
|
|
248
|
+
<Text fg={colors.textDim}>{selectedUsernames.length > 1 ? t("Group chat") : t("Direct message")}</Text>
|
|
248
249
|
)}
|
|
249
250
|
<Box flexGrow={1} />
|
|
250
251
|
<Box
|
|
@@ -255,7 +256,7 @@ export function NewDmDialog({
|
|
|
255
256
|
style={{ cursor: canSubmit ? "pointer" : "default" }}
|
|
256
257
|
>
|
|
257
258
|
<Text fg={canSubmit ? colors.selectedText : colors.textDim}>
|
|
258
|
-
{submitting ? "
|
|
259
|
+
{submitting ? ` ${t("Starting")} ` : ` ${t("Start")} `}
|
|
259
260
|
</Text>
|
|
260
261
|
</Box>
|
|
261
262
|
</Box>
|
|
@@ -2,6 +2,7 @@ import { Box, ScrollBox, Text, type ScrollBoxRenderable } from "../../../../ui";
|
|
|
2
2
|
import type { Dispatch, SetStateAction } from "react";
|
|
3
3
|
import type { InlineTickerCatalogEntry } from "../../../../state/hooks/inline-tickers";
|
|
4
4
|
import { colors } from "../../../../theme/colors";
|
|
5
|
+
import { t } from "../../../../i18n";
|
|
5
6
|
import type { ChatMessage, ChatUserSummary } from "../../../../api-client";
|
|
6
7
|
import { DesktopChatMessage } from "../message/desktop";
|
|
7
8
|
import { UserProfilePopover } from "../message/profile-popover";
|
|
@@ -37,6 +38,7 @@ interface ChatTranscriptProps {
|
|
|
37
38
|
selectedIdx: number;
|
|
38
39
|
setHoveredIdx: Dispatch<SetStateAction<number | null>>;
|
|
39
40
|
showProfilePopover: (user: ChatUserSummary) => void;
|
|
41
|
+
onSetUpProfile: () => void;
|
|
40
42
|
stickyTranscript: boolean;
|
|
41
43
|
user: { id: string; username: string; emailVerified: boolean } | null;
|
|
42
44
|
userByUsername: Map<string, ChatUserSummary>;
|
|
@@ -69,7 +71,9 @@ export function ChatTranscript({
|
|
|
69
71
|
setHoveredIdx,
|
|
70
72
|
showProfilePopover,
|
|
71
73
|
stickyTranscript,
|
|
74
|
+
user,
|
|
72
75
|
userByUsername,
|
|
76
|
+
onSetUpProfile,
|
|
73
77
|
}: ChatTranscriptProps) {
|
|
74
78
|
return (
|
|
75
79
|
<>
|
|
@@ -86,16 +90,16 @@ export function ChatTranscript({
|
|
|
86
90
|
>
|
|
87
91
|
{loadingOlderMessages && (
|
|
88
92
|
<Box alignItems="center" justifyContent="center" height={1} width={contentWidth}>
|
|
89
|
-
<Text fg={colors.textDim}>Loading earlier messages
|
|
93
|
+
<Text fg={colors.textDim}>{t("Loading earlier messages...")}</Text>
|
|
90
94
|
</Box>
|
|
91
95
|
)}
|
|
92
96
|
{loading && messages.length === 0 ? (
|
|
93
97
|
<Box alignItems="center" justifyContent="center" flexGrow={1}>
|
|
94
|
-
<Text fg={colors.textDim}>Loading
|
|
98
|
+
<Text fg={colors.textDim}>{t("Loading...")}</Text>
|
|
95
99
|
</Box>
|
|
96
100
|
) : messages.length === 0 && (
|
|
97
101
|
<Box alignItems="center" justifyContent="center" flexGrow={1}>
|
|
98
|
-
<Text fg={colors.textDim}>No messages yet. Be the first to say something
|
|
102
|
+
<Text fg={colors.textDim}>{t("No messages yet. Be the first to say something!")}</Text>
|
|
99
103
|
</Box>
|
|
100
104
|
)}
|
|
101
105
|
{messages.map((msg, index) => (
|
|
@@ -151,6 +155,8 @@ export function ChatTranscript({
|
|
|
151
155
|
width={chatWidth}
|
|
152
156
|
onClose={scheduleProfilePopoverClose}
|
|
153
157
|
onKeepOpen={cancelProfilePopoverClose}
|
|
158
|
+
isOwnProfile={profilePopoverUser.id === user?.id}
|
|
159
|
+
onSetUpProfile={onSetUpProfile}
|
|
154
160
|
/>
|
|
155
161
|
)}
|
|
156
162
|
</>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ChatChannel, ChatMessage } from "../../../../api-client";
|
|
2
|
+
import { t, tf } from "../../../../i18n";
|
|
2
3
|
|
|
3
4
|
const ISO_TIMESTAMP_CURSOR = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z$/;
|
|
4
5
|
const USERNAME_MENTION = /(^|[^A-Za-z0-9_])@([A-Za-z][A-Za-z0-9_]{2,29})(?![A-Za-z0-9_])/g;
|
|
@@ -25,30 +26,30 @@ function formatMessageSnippet(content: string): string {
|
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export function formatMentionToast(message: ChatMessage): string {
|
|
28
|
-
const author = message.user.username || "Someone";
|
|
29
|
+
const author = message.user.username || t("Someone");
|
|
29
30
|
const snippet = formatMessageSnippet(message.content);
|
|
30
31
|
if (!snippet) {
|
|
31
|
-
return
|
|
32
|
+
return tf("@{author} mentioned you in chat.", { author });
|
|
32
33
|
}
|
|
33
|
-
return
|
|
34
|
+
return tf("@{author} mentioned you: {snippet}", { author, snippet });
|
|
34
35
|
}
|
|
35
36
|
|
|
36
37
|
export function formatReplyToast(message: ChatMessage): string {
|
|
37
|
-
const author = message.user.username || "Someone";
|
|
38
|
+
const author = message.user.username || t("Someone");
|
|
38
39
|
const snippet = formatMessageSnippet(message.content);
|
|
39
40
|
if (!snippet) {
|
|
40
|
-
return
|
|
41
|
+
return tf("@{author} replied to you.", { author });
|
|
41
42
|
}
|
|
42
|
-
return
|
|
43
|
+
return tf("@{author} replied to you: {snippet}", { author, snippet });
|
|
43
44
|
}
|
|
44
45
|
|
|
45
46
|
export function formatChannelToast(channelTitle: string, message: ChatMessage, channelKind?: ChatChannel["kind"]): string {
|
|
46
|
-
const author = message.user.username || "Someone";
|
|
47
|
+
const author = message.user.username || t("Someone");
|
|
47
48
|
const snippet = formatMessageSnippet(message.content);
|
|
48
49
|
if (channelKind === "direct") {
|
|
49
|
-
return snippet ? `@${author}: ${snippet}` :
|
|
50
|
+
return snippet ? `@${author}: ${snippet}` : tf("@{author} sent a message.", { author });
|
|
50
51
|
}
|
|
51
|
-
return snippet ? `${channelTitle} @${author}: ${snippet}` :
|
|
52
|
+
return snippet ? `${channelTitle} @${author}: ${snippet}` : tf("{channel} @{author} sent a message.", { channel: channelTitle, author });
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
export function isLegacyTimestampCursor(cursor: string | null): boolean {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { memo } from "react";
|
|
2
2
|
import { Box, Span, Text } from "../../../../ui";
|
|
3
3
|
import { hoverBg } from "../../../../theme/colors";
|
|
4
|
+
import { t } from "../../../../i18n";
|
|
4
5
|
import { MESSAGE_ACTION_WIDTH, normalizeInlinePreview } from "../layout";
|
|
5
6
|
import { ChatActionChip } from "./action-chip";
|
|
6
7
|
import { ResponsiveTickerBadgeText } from "./inline-tokens";
|
|
@@ -35,6 +36,7 @@ export const DesktopChatMessage = memo(function DesktopChatMessage({
|
|
|
35
36
|
const showInlineEditAction = !state.grouped && canSend && canEditMessage;
|
|
36
37
|
const showGroupedReplyAction = state.grouped && canSend;
|
|
37
38
|
const showGroupedEditAction = state.grouped && canSend && canEditMessage;
|
|
39
|
+
const authorLabel = msg.user.username ?? "anon";
|
|
38
40
|
const groupedActionWidth = MESSAGE_ACTION_WIDTH * Number(showGroupedReplyAction) + MESSAGE_ACTION_WIDTH * Number(showGroupedEditAction);
|
|
39
41
|
const rowProps = {
|
|
40
42
|
width: "100%",
|
|
@@ -94,15 +96,20 @@ export const DesktopChatMessage = memo(function DesktopChatMessage({
|
|
|
94
96
|
height={1}
|
|
95
97
|
paddingLeft={1}
|
|
96
98
|
>
|
|
97
|
-
<
|
|
98
|
-
|
|
99
|
-
attributes={state.authorAttributes}
|
|
99
|
+
<Box
|
|
100
|
+
height={1}
|
|
100
101
|
onMouseOver={() => onUserHover(msg.user)}
|
|
102
|
+
onMouseMove={() => onUserHover(msg.user)}
|
|
101
103
|
onMouseOut={onUserHoverEnd}
|
|
102
|
-
style={{ cursor: "
|
|
104
|
+
style={{ cursor: "pointer" }}
|
|
103
105
|
>
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
<Text
|
|
107
|
+
fg={state.authorColor}
|
|
108
|
+
attributes={state.authorAttributes}
|
|
109
|
+
>
|
|
110
|
+
{authorLabel}
|
|
111
|
+
</Text>
|
|
112
|
+
</Box>
|
|
106
113
|
<Text fg={state.headerStatusColor}> {state.headerStatus}</Text>
|
|
107
114
|
{(showInlineReplyAction || showInlineEditAction) && (
|
|
108
115
|
<>
|
|
@@ -114,7 +121,7 @@ export const DesktopChatMessage = memo(function DesktopChatMessage({
|
|
|
114
121
|
data-gloom-role="chat-message-reply-action"
|
|
115
122
|
>
|
|
116
123
|
<ChatActionChip
|
|
117
|
-
label="Reply"
|
|
124
|
+
label={t("Reply")}
|
|
118
125
|
width={MESSAGE_ACTION_WIDTH}
|
|
119
126
|
emphasized={state.isSelected}
|
|
120
127
|
onPress={() => beginReplyTo(index)}
|
|
@@ -128,7 +135,7 @@ export const DesktopChatMessage = memo(function DesktopChatMessage({
|
|
|
128
135
|
data-gloom-role="chat-message-reply-action"
|
|
129
136
|
>
|
|
130
137
|
<ChatActionChip
|
|
131
|
-
label="Edit"
|
|
138
|
+
label={t("Edit")}
|
|
132
139
|
width={MESSAGE_ACTION_WIDTH}
|
|
133
140
|
emphasized={state.isSelected}
|
|
134
141
|
onPress={() => beginEditMessage(index)}
|
|
@@ -169,7 +176,7 @@ export const DesktopChatMessage = memo(function DesktopChatMessage({
|
|
|
169
176
|
>
|
|
170
177
|
{showGroupedReplyAction && (
|
|
171
178
|
<ChatActionChip
|
|
172
|
-
label="Reply"
|
|
179
|
+
label={t("Reply")}
|
|
173
180
|
width={MESSAGE_ACTION_WIDTH}
|
|
174
181
|
emphasized={state.isSelected}
|
|
175
182
|
onPress={() => beginReplyTo(index)}
|
|
@@ -177,7 +184,7 @@ export const DesktopChatMessage = memo(function DesktopChatMessage({
|
|
|
177
184
|
)}
|
|
178
185
|
{showGroupedEditAction && (
|
|
179
186
|
<ChatActionChip
|
|
180
|
-
label="Edit"
|
|
187
|
+
label={t("Edit")}
|
|
181
188
|
width={MESSAGE_ACTION_WIDTH}
|
|
182
189
|
emphasized={state.isSelected}
|
|
183
190
|
onPress={() => beginEditMessage(index)}
|
|
@@ -1,6 +1,30 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import
|
|
3
|
-
import {
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
|
+
import { act, useEffect } from "react";
|
|
3
|
+
import {
|
|
4
|
+
apiClient,
|
|
5
|
+
type AccountProfile,
|
|
6
|
+
type ChatUserSummary,
|
|
7
|
+
} from "../../../../api-client";
|
|
8
|
+
import { testRender } from "../../../../renderers/opentui/test-utils";
|
|
9
|
+
import { Box, Text } from "../../../../ui";
|
|
10
|
+
import { useChatProfilePopover } from "../profile-popover";
|
|
11
|
+
import {
|
|
12
|
+
hasPublicChatProfileInfo,
|
|
13
|
+
shouldOfferChatProfileSetup,
|
|
14
|
+
} from "./profile-popover";
|
|
15
|
+
|
|
16
|
+
let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
|
|
17
|
+
const originalGetAccountProfile = apiClient.getAccountProfile.bind(apiClient);
|
|
18
|
+
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
apiClient.getAccountProfile = originalGetAccountProfile;
|
|
21
|
+
apiClient.setSessionToken(null);
|
|
22
|
+
if (!testSetup) return;
|
|
23
|
+
await act(async () => {
|
|
24
|
+
testSetup?.renderer.destroy();
|
|
25
|
+
});
|
|
26
|
+
testSetup = undefined;
|
|
27
|
+
});
|
|
4
28
|
|
|
5
29
|
function makeUser(overrides: Partial<ChatUserSummary>): ChatUserSummary {
|
|
6
30
|
return {
|
|
@@ -12,6 +36,55 @@ function makeUser(overrides: Partial<ChatUserSummary>): ChatUserSummary {
|
|
|
12
36
|
};
|
|
13
37
|
}
|
|
14
38
|
|
|
39
|
+
function makeAccountProfile(overrides: Partial<AccountProfile> = {}): AccountProfile {
|
|
40
|
+
return {
|
|
41
|
+
id: "u1",
|
|
42
|
+
email: "vince@example.com",
|
|
43
|
+
emailVerified: true,
|
|
44
|
+
plan: "pro",
|
|
45
|
+
username: "vince",
|
|
46
|
+
name: "Vince",
|
|
47
|
+
company: "Gloom",
|
|
48
|
+
title: null,
|
|
49
|
+
bio: "Made Gloomberb",
|
|
50
|
+
profilePublic: true,
|
|
51
|
+
publicEmail: null,
|
|
52
|
+
xAccount: null,
|
|
53
|
+
sharedPortfolioId: "broker:ibkr:coldstart",
|
|
54
|
+
acceptUnknownDms: false,
|
|
55
|
+
chatEmailNotificationsEnabled: true,
|
|
56
|
+
portfolioAnalytics: null,
|
|
57
|
+
syncEnabled: true,
|
|
58
|
+
weeklyRoundupEnabled: true,
|
|
59
|
+
positionAlertsEnabled: true,
|
|
60
|
+
lastSyncAt: null,
|
|
61
|
+
lastRoundupEmailAt: null,
|
|
62
|
+
updatedAt: "2026-07-21T22:03:59.832Z",
|
|
63
|
+
...overrides,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function OwnProfileHarness({ user }: { user: ChatUserSummary }) {
|
|
68
|
+
const {
|
|
69
|
+
profilePopoverUser,
|
|
70
|
+
showProfilePopover,
|
|
71
|
+
} = useChatProfilePopover();
|
|
72
|
+
|
|
73
|
+
useEffect(() => {
|
|
74
|
+
showProfilePopover(user, { ownProfile: true });
|
|
75
|
+
}, [showProfilePopover, user]);
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<Box width={50} height={1}>
|
|
79
|
+
<Text>
|
|
80
|
+
{profilePopoverUser?.portfolioAnalytics
|
|
81
|
+
? JSON.stringify(profilePopoverUser.portfolioAnalytics)
|
|
82
|
+
: "loading"}
|
|
83
|
+
</Text>
|
|
84
|
+
</Box>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
|
|
15
88
|
describe("profile popover", () => {
|
|
16
89
|
test("treats public portfolio analytics as hover profile information", () => {
|
|
17
90
|
expect(hasPublicChatProfileInfo(makeUser({
|
|
@@ -33,4 +106,50 @@ describe("profile popover", () => {
|
|
|
33
106
|
},
|
|
34
107
|
}))).toBe(false);
|
|
35
108
|
});
|
|
109
|
+
|
|
110
|
+
test("refreshes your cached chat identity from the current account profile", async () => {
|
|
111
|
+
apiClient.setSessionToken("token-123");
|
|
112
|
+
apiClient.getAccountProfile = async () => makeAccountProfile({
|
|
113
|
+
portfolioAnalytics: {
|
|
114
|
+
oneYearReturn: 0.14,
|
|
115
|
+
spyBeta: 1.05,
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
await act(async () => {
|
|
120
|
+
testSetup = await testRender(
|
|
121
|
+
<OwnProfileHarness user={makeUser({
|
|
122
|
+
company: "Gloom",
|
|
123
|
+
bio: "Made Gloomberb",
|
|
124
|
+
portfolioAnalytics: null,
|
|
125
|
+
})} />,
|
|
126
|
+
{ width: 50, height: 8 },
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
const setup = testSetup;
|
|
130
|
+
expect(setup).toBeDefined();
|
|
131
|
+
if (!setup) return;
|
|
132
|
+
await act(async () => {
|
|
133
|
+
await setup.renderOnce();
|
|
134
|
+
await Promise.resolve();
|
|
135
|
+
await setup.renderOnce();
|
|
136
|
+
await setup.renderOnce();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const frame = setup.captureCharFrame();
|
|
140
|
+
expect(frame).toContain('"oneYearReturn":0.14');
|
|
141
|
+
expect(frame).toContain('"spyBeta":1.05');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("offers a quiet setup action on your own empty profile", () => {
|
|
145
|
+
const emptyProfile = makeUser({
|
|
146
|
+
bio: null,
|
|
147
|
+
company: null,
|
|
148
|
+
title: null,
|
|
149
|
+
profilePublic: false,
|
|
150
|
+
});
|
|
151
|
+
expect(shouldOfferChatProfileSetup(emptyProfile, true)).toBe(true);
|
|
152
|
+
expect(shouldOfferChatProfileSetup(emptyProfile, false)).toBe(false);
|
|
153
|
+
expect(shouldOfferChatProfileSetup(makeUser({ bio: "Already set up" }), true)).toBe(false);
|
|
154
|
+
});
|
|
36
155
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import { useState } from "react";
|
|
1
2
|
import { Box, Text } from "../../../../ui";
|
|
2
3
|
import { TextAttributes } from "../../../../ui";
|
|
3
|
-
import { colors } from "../../../../theme/colors";
|
|
4
|
+
import { colors, hoverBg } from "../../../../theme/colors";
|
|
4
5
|
import type { ChatUserSummary, PublicPortfolioAnalytics } from "../../../../api-client";
|
|
5
6
|
import { formatNumber } from "../../../../utils/format";
|
|
6
7
|
import { truncateChannelLabel } from "../channels";
|
|
@@ -22,6 +23,14 @@ export function hasPublicChatProfileInfo(user: ChatUserSummary): boolean {
|
|
|
22
23
|
return Boolean(user.bio?.trim() || user.title?.trim() || user.company?.trim() || hasPortfolioAnalytics(user.portfolioAnalytics));
|
|
23
24
|
}
|
|
24
25
|
|
|
26
|
+
export function hasChatProfileDetails(user: ChatUserSummary): boolean {
|
|
27
|
+
return Boolean(user.bio?.trim() || user.title?.trim() || user.company?.trim());
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function shouldOfferChatProfileSetup(user: ChatUserSummary, isOwnProfile: boolean): boolean {
|
|
31
|
+
return isOwnProfile && !hasChatProfileDetails(user);
|
|
32
|
+
}
|
|
33
|
+
|
|
25
34
|
function formatSignedPercent(value: number): string {
|
|
26
35
|
const percent = value * 100;
|
|
27
36
|
return `${percent >= 0 ? "+" : ""}${formatNumber(percent, 2)}%`;
|
|
@@ -118,12 +127,17 @@ export function UserProfilePopover({
|
|
|
118
127
|
width,
|
|
119
128
|
onClose,
|
|
120
129
|
onKeepOpen,
|
|
130
|
+
isOwnProfile = false,
|
|
131
|
+
onSetUpProfile,
|
|
121
132
|
}: {
|
|
122
133
|
user: ChatUserSummary;
|
|
123
134
|
width: number;
|
|
124
135
|
onClose: () => void;
|
|
125
136
|
onKeepOpen: () => void;
|
|
137
|
+
isOwnProfile?: boolean;
|
|
138
|
+
onSetUpProfile?: () => void;
|
|
126
139
|
}) {
|
|
140
|
+
const [setupHovered, setSetupHovered] = useState(false);
|
|
127
141
|
const popoverWidth = Math.max(24, Math.min(38, width - 4));
|
|
128
142
|
const meta = [user.title, user.company].filter(Boolean).join(" · ");
|
|
129
143
|
const bio = user.bio?.trim();
|
|
@@ -135,6 +149,7 @@ export function UserProfilePopover({
|
|
|
135
149
|
? Math.min(headerMetricsNaturalWidth(metrics), maxStatsWidth)
|
|
136
150
|
: 0;
|
|
137
151
|
const usernameWidth = Math.max(1, headerWidth - (statsWidth > 0 ? statsWidth + 1 : 0));
|
|
152
|
+
const showSetupAction = shouldOfferChatProfileSetup(user, isOwnProfile) && !!onSetUpProfile;
|
|
138
153
|
|
|
139
154
|
return (
|
|
140
155
|
<Box
|
|
@@ -171,6 +186,28 @@ export function UserProfilePopover({
|
|
|
171
186
|
{bio}
|
|
172
187
|
</Text>
|
|
173
188
|
) : null}
|
|
189
|
+
{showSetupAction ? (
|
|
190
|
+
<Box
|
|
191
|
+
height={1}
|
|
192
|
+
width={headerWidth}
|
|
193
|
+
backgroundColor={setupHovered ? hoverBg() : undefined}
|
|
194
|
+
onMouseOver={() => setSetupHovered(true)}
|
|
195
|
+
onMouseOut={() => setSetupHovered(false)}
|
|
196
|
+
onMouseDown={(event: { preventDefault?: () => void; stopPropagation?: () => void }) => {
|
|
197
|
+
event?.preventDefault?.();
|
|
198
|
+
event?.stopPropagation?.();
|
|
199
|
+
onSetUpProfile?.();
|
|
200
|
+
}}
|
|
201
|
+
style={{ cursor: "pointer" }}
|
|
202
|
+
>
|
|
203
|
+
<Text
|
|
204
|
+
fg={setupHovered ? colors.text : colors.textMuted}
|
|
205
|
+
attributes={setupHovered ? TextAttributes.BOLD : 0}
|
|
206
|
+
>
|
|
207
|
+
Set up profile
|
|
208
|
+
</Text>
|
|
209
|
+
</Box>
|
|
210
|
+
) : null}
|
|
174
211
|
</Box>
|
|
175
212
|
);
|
|
176
213
|
}
|