gloomberb 0.11.1 → 0.11.3
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 +11 -9
- package/README.zh-CN.md +6 -6
- package/package.json +1 -1
- package/src/api-client/data.ts +12 -0
- package/src/api-client/index.test.ts +47 -0
- package/src/api-client/index.ts +66 -0
- package/src/api-client/paths.ts +16 -0
- package/src/api-client/types.ts +59 -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.tsx +9 -2
- package/src/capabilities/chart-series.ts +3 -2
- package/src/cli/commands/automation.test.ts +9 -0
- package/src/cli/commands/automation.ts +22 -73
- package/src/cli/commands/market.ts +2 -2
- package/src/cli/index.ts +0 -2
- package/src/cli/pane-functions/capabilities.ts +3 -3
- package/src/cli/registry.ts +0 -22
- package/src/cli/result.ts +7 -10
- package/src/components/chart/composite/composite-chart.test.tsx +45 -2
- package/src/components/chart/composite/composite-chart.tsx +12 -9
- package/src/components/chart/composite/scene.ts +0 -21
- package/src/components/chart/composite/tools.ts +49 -0
- 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/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/data-table/export.test.ts +21 -0
- package/src/components/data-table/export.ts +21 -0
- package/src/components/layout/detached-pane-shell.tsx +74 -10
- 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 +86 -30
- 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/ui/data-table/index.tsx +19 -2
- 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 +5 -5
- 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 +3 -4
- package/src/i18n/ja.ts +3 -4
- package/src/i18n/ko.ts +3 -4
- package/src/i18n/zh-cn.ts +3 -4
- package/src/i18n/zh-tw.ts +3 -4
- 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 +406 -0
- package/src/layout-marketplace/gallery.test.tsx +148 -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/hooks.tsx +0 -41
- package/src/plugins/broker-sync/broker-sync.test.ts +29 -0
- package/src/plugins/broker-sync/normalize.ts +9 -2
- 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 +16 -1
- package/src/plugins/builtin/alerts/alert-engine.ts +4 -0
- package/src/plugins/builtin/alerts/index.tsx +4 -0
- package/src/plugins/builtin/alerts/pane.tsx +2 -8
- package/src/plugins/builtin/analytics/index.tsx +3 -0
- package/src/plugins/builtin/broker-manager/index.tsx +3 -0
- 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/cli-options.ts +12 -9
- 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 +81 -14
- package/src/plugins/builtin/chart-composer/settings.ts +2 -13
- package/src/plugins/builtin/chat/content/index.tsx +4 -1
- package/src/plugins/builtin/chat/controller/index.ts +8 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +14 -2
- package/src/plugins/builtin/chat/controller/utils.ts +4 -6
- package/src/plugins/builtin/chat/controller.test.ts +64 -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-commands.ts +0 -4
- package/src/plugins/builtin/cloud/auth-model.ts +4 -1
- package/src/plugins/builtin/cloud/plugin.tsx +11 -1
- 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 +44 -5
- package/src/plugins/builtin/composite-plugins.ts +5 -1
- package/src/plugins/builtin/connections/index.ts +3 -0
- package/src/plugins/builtin/earnings/index.tsx +1 -0
- package/src/plugins/builtin/econ/calendar-model.ts +0 -5
- package/src/plugins/builtin/econ/index.tsx +1 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
- package/src/plugins/builtin/futures/index.tsx +1 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +1 -0
- package/src/plugins/builtin/help/index.tsx +2 -2
- package/src/plugins/builtin/ipo-calendar/index.tsx +1 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
- package/src/plugins/builtin/layout-manager/index.tsx +17 -4
- package/src/plugins/builtin/market-halts/index.tsx +1 -0
- package/src/plugins/builtin/market-movers/index.tsx +1 -0
- package/src/plugins/builtin/news/index.tsx +1 -0
- 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 +2 -2
- package/src/plugins/builtin/options/analytics.test.ts +83 -0
- package/src/plugins/builtin/options/analytics.ts +110 -0
- package/src/plugins/builtin/options/index.tsx +18 -2
- package/src/plugins/builtin/options/table.ts +121 -51
- package/src/plugins/builtin/options/types.ts +22 -15
- package/src/plugins/builtin/options/view.test.tsx +49 -3
- package/src/plugins/builtin/options/view.tsx +92 -19
- package/src/plugins/builtin/plugin-module.test.ts +2 -4
- package/src/plugins/builtin/plugin-module.ts +0 -7
- package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +2 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
- 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 +9 -0
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
- package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +21 -16
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
- package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +33 -7
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +109 -12
- package/src/plugins/builtin/research/index.tsx +4 -0
- package/src/plugins/builtin/scanner/index.ts +2 -0
- package/src/plugins/builtin/sectors/index.tsx +1 -0
- 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/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/ticker-detail/data-panes/historical-prices.tsx +3 -5
- package/src/plugins/builtin/ticker-detail/index.tsx +12 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +3 -0
- package/src/plugins/builtin/world-indices/index.tsx +3 -0
- package/src/plugins/builtin/world-venue-map/index.ts +27 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +337 -0
- package/src/plugins/builtin/world-venue-map/model.test.ts +53 -0
- package/src/plugins/builtin/world-venue-map/model.ts +147 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +300 -0
- package/src/plugins/builtin/world-venue-map/world-outlines.ts +2 -0
- package/src/plugins/catalog-backend.ts +6 -44
- package/src/plugins/catalog-browser.test.ts +10 -0
- package/src/plugins/catalog-browser.ts +5 -1
- package/src/plugins/gridlock-notification.ts +2 -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/index.ts +5 -1
- 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 +4 -0
- package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
- package/src/plugins/registry/index.ts +2 -0
- package/src/plugins/registry/pane-settings.test.ts +53 -0
- package/src/plugins/registry/pane-settings.ts +36 -3
- 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/cloud-transport.test.ts +36 -0
- package/src/renderers/browser/cloud-transport.ts +19 -0
- package/src/renderers/browser/config-host.ts +0 -4
- package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
- package/src/renderers/browser/deeplink-bridge.ts +22 -0
- package/src/renderers/browser/main.tsx +3 -0
- package/src/renderers/browser/ui-host.tsx +11 -0
- package/src/renderers/cloudflare/worker.test.ts +8 -5
- package/src/renderers/cloudflare/worker.ts +6 -5
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -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/bun/index.ts +1 -0
- package/src/renderers/electrobun/shared/protocol.ts +4 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/host/native.ts +0 -5
- 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 +7 -1
- package/src/renderers/opentui/host.tsx +4 -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 +54 -6
- package/src/renderers/share/styles.css +35 -0
- package/src/renderers/share/view.test.tsx +78 -0
- package/src/renderers/share/view.tsx +71 -0
- package/src/shares/api.test.ts +52 -3
- package/src/shares/api.ts +39 -15
- 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 +74 -1
- 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/pane-table-export-registry.ts +31 -0
- package/src/state/selectors-ui.ts +0 -8
- package/src/sync/controller.test.ts +57 -1
- package/src/sync/controller.ts +22 -11
- package/src/sync/react.ts +13 -0
- package/src/theme/font-scale.ts +0 -11
- package/src/time-series/range.ts +12 -0
- package/src/time-series/resolution.ts +3 -15
- package/src/time-series/spec.ts +5 -5
- package/src/types/config.ts +35 -5
- package/src/types/desktop-menu.ts +1 -0
- package/src/types/plugin.ts +38 -20
- package/src/ui/context-menu.tsx +1 -1
- package/src/ui/host.tsx +7 -0
- package/src/ui/toast.tsx +2 -0
- package/src/utils/back-navigation.ts +0 -4
- package/src/utils/csv.test.ts +13 -0
- package/src/utils/csv.ts +37 -0
- package/src/utils/save-text-file.test.ts +24 -0
- package/src/utils/save-text-file.ts +36 -0
|
@@ -179,17 +179,17 @@ describe("CommandBar", () => {
|
|
|
179
179
|
expect(testSetup.captureCharFrame()).not.toContain("GitHub Light");
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
-
test("
|
|
182
|
+
test("finds the tidy windows command by its snap alias", async () => {
|
|
183
183
|
const calls: string[] = [];
|
|
184
184
|
|
|
185
185
|
testSetup = await testRender(<CommandBarHarness
|
|
186
|
-
query="
|
|
186
|
+
query="snap"
|
|
187
187
|
configurePluginRegistry={(pluginRegistry) => {
|
|
188
188
|
(pluginRegistry.commands as Map<string, any>).set("gridlock-all", {
|
|
189
189
|
id: "gridlock-all",
|
|
190
|
-
label: "
|
|
191
|
-
description: "Arrange
|
|
192
|
-
keywords: ["grid", "gridlock", "tile", "arrange", "windows", "layout"],
|
|
190
|
+
label: "Tidy Windows",
|
|
191
|
+
description: "Arrange every window into one tiled layout",
|
|
192
|
+
keywords: ["tidy", "snap", "grid", "gridlock", "tile", "arrange", "organize", "organise", "cleanup", "dock", "floating", "windows", "layout"],
|
|
193
193
|
shortcut: "GL",
|
|
194
194
|
category: "config",
|
|
195
195
|
execute: async () => {
|
|
@@ -215,7 +215,7 @@ describe("CommandBar", () => {
|
|
|
215
215
|
await testSetup.renderOnce();
|
|
216
216
|
|
|
217
217
|
const frame = testSetup.captureCharFrame();
|
|
218
|
-
expect(frame).toContain("
|
|
218
|
+
expect(frame).toContain("Tidy Windows");
|
|
219
219
|
expect(frame).toContain("GL");
|
|
220
220
|
|
|
221
221
|
await act(async () => {
|
|
@@ -142,6 +142,23 @@ function registerOptionalTextPane(pluginRegistry: MutablePaneRegistry): void {
|
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
function registerOptionalTickerPane(pluginRegistry: MutablePaneRegistry): void {
|
|
146
|
+
mutableRegistryMap(pluginRegistry.panes).set("optional-ticker", {
|
|
147
|
+
id: "optional-ticker",
|
|
148
|
+
name: "Optional Ticker",
|
|
149
|
+
component: () => null,
|
|
150
|
+
defaultPosition: "right",
|
|
151
|
+
defaultMode: "floating",
|
|
152
|
+
});
|
|
153
|
+
mutableRegistryMap(pluginRegistry.paneTemplates).set("optional-ticker-pane", {
|
|
154
|
+
id: "optional-ticker-pane",
|
|
155
|
+
paneId: "optional-ticker",
|
|
156
|
+
label: "Optional Ticker",
|
|
157
|
+
description: "Open market-wide or for one ticker.",
|
|
158
|
+
shortcut: { prefix: "OT", argPlaceholder: "ticker", argKind: "ticker", argOptional: true },
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
145
162
|
function registerQueryOnlyPane(pluginRegistry: MutablePaneRegistry): void {
|
|
146
163
|
mutableRegistryMap(pluginRegistry.panes).set("prediction-markets", {
|
|
147
164
|
id: "prediction-markets",
|
|
@@ -204,7 +221,7 @@ describe("CommandBar pane and layout routes", () => {
|
|
|
204
221
|
|
|
205
222
|
test("renders layout mode with focused pane actions", async () => {
|
|
206
223
|
testSetup = await testRender(<CommandBarHarness
|
|
207
|
-
query="
|
|
224
|
+
query="LMA "
|
|
208
225
|
configureConfig={layoutModeConfig}
|
|
209
226
|
configureState={layoutModeState}
|
|
210
227
|
/>, {
|
|
@@ -248,9 +265,29 @@ describe("CommandBar pane and layout routes", () => {
|
|
|
248
265
|
expect(actions.some((action) => action.type === "UNDO_LAYOUT")).toBe(true);
|
|
249
266
|
});
|
|
250
267
|
|
|
268
|
+
test("opens Layouts as a normal pane from LAY", async () => {
|
|
269
|
+
const opened: string[] = [];
|
|
270
|
+
testSetup = await testRender(
|
|
271
|
+
<CommandBarHarness
|
|
272
|
+
query="LAY"
|
|
273
|
+
live
|
|
274
|
+
configurePluginRegistry={(pluginRegistry) => {
|
|
275
|
+
pluginRegistry.showPane = (paneId) => opened.push(paneId);
|
|
276
|
+
}}
|
|
277
|
+
/>,
|
|
278
|
+
{ width: 90, height: 18 },
|
|
279
|
+
);
|
|
280
|
+
await testSetup.renderOnce();
|
|
281
|
+
expect(testSetup.captureCharFrame()).toContain("Layouts");
|
|
282
|
+
|
|
283
|
+
await clickFrameText("Layouts");
|
|
284
|
+
|
|
285
|
+
expect(opened).toEqual(["layout-marketplace"]);
|
|
286
|
+
});
|
|
287
|
+
|
|
251
288
|
test("renders filtered saved layouts with textual previews", async () => {
|
|
252
289
|
testSetup = await testRender(<CommandBarHarness
|
|
253
|
-
query="
|
|
290
|
+
query="LMA Research"
|
|
254
291
|
configureConfig={layoutModeConfig}
|
|
255
292
|
configureState={layoutModeState}
|
|
256
293
|
/>, {
|
|
@@ -353,6 +390,33 @@ describe("CommandBar pane and layout routes", () => {
|
|
|
353
390
|
expect(testSetup.captureCharFrame()).not.toContain("Create Pane");
|
|
354
391
|
});
|
|
355
392
|
|
|
393
|
+
test("keeps an optional ticker shortcut market-wide when a ticker is active", async () => {
|
|
394
|
+
const created: CreatedPaneCall[] = [];
|
|
395
|
+
|
|
396
|
+
testSetup = await testRender(<CommandBarHarness
|
|
397
|
+
query="OT"
|
|
398
|
+
selectedTicker="AAPL"
|
|
399
|
+
live
|
|
400
|
+
configurePluginRegistry={(pluginRegistry) => {
|
|
401
|
+
registerOptionalTickerPane(pluginRegistry);
|
|
402
|
+
recordPaneCreations(pluginRegistry, created);
|
|
403
|
+
}}
|
|
404
|
+
/>, {
|
|
405
|
+
width: 100,
|
|
406
|
+
height: 18,
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
await testSetup.renderOnce();
|
|
410
|
+
|
|
411
|
+
await act(async () => {
|
|
412
|
+
testSetup!.mockInput.pressEnter();
|
|
413
|
+
await Bun.sleep(0);
|
|
414
|
+
await testSetup!.renderOnce();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
expect(created).toEqual([{ templateId: "optional-ticker-pane", options: undefined }]);
|
|
418
|
+
});
|
|
419
|
+
|
|
356
420
|
for (const scenario of [
|
|
357
421
|
{
|
|
358
422
|
query: "prediction markets",
|
|
@@ -14,7 +14,8 @@ describe("command bar view model helpers", () => {
|
|
|
14
14
|
expect(resolveCommandBarMode("T NVDA")).toMatchObject({ kind: "search", badge: "T" });
|
|
15
15
|
expect(resolveCommandBarMode("TH ")).toMatchObject({ kind: "themes", badge: "THEMES" });
|
|
16
16
|
expect(resolveCommandBarMode("PL notes")).toMatchObject({ kind: "plugins", badge: "PLUGINS" });
|
|
17
|
-
expect(resolveCommandBarMode("LAY
|
|
17
|
+
expect(resolveCommandBarMode("LAY")).toMatchObject({ kind: "direct-command", badge: "COMMAND" });
|
|
18
|
+
expect(resolveCommandBarMode("LMA ")).toMatchObject({ kind: "layout", badge: "LAYOUT" });
|
|
18
19
|
expect(resolveCommandBarMode("NP ")).toMatchObject({ kind: "default", badge: "FILTER" });
|
|
19
20
|
expect(resolveCommandBarMode("PS")).toMatchObject({ kind: "direct-command", badge: "COMMAND" });
|
|
20
21
|
expect(resolveCommandBarMode("AW")).toMatchObject({ kind: "direct-command", badge: "COMMAND" });
|
|
@@ -3,6 +3,7 @@ import { cloneLayout, createDefaultConfig, findPaneInstance, type LayoutConfig }
|
|
|
3
3
|
import { createInitialState } from "../../../state/app/context";
|
|
4
4
|
import { createTestDataProvider } from "../../../test-support/data-provider";
|
|
5
5
|
import { applyPaneSettingFieldValue, createPaneTemplateOrThrow } from "./ops";
|
|
6
|
+
import type { TickerRecord } from "../../../types/ticker";
|
|
6
7
|
|
|
7
8
|
function makeDataProvider() {
|
|
8
9
|
return createTestDataProvider({ id: "test" });
|
|
@@ -68,6 +69,59 @@ describe("createPaneTemplateOrThrow", () => {
|
|
|
68
69
|
expect(placeCalls).toHaveLength(0);
|
|
69
70
|
});
|
|
70
71
|
|
|
72
|
+
test("uses an explicit shared symbol instead of the recipient's active ticker", async () => {
|
|
73
|
+
const config = createDefaultConfig("/tmp/gloomberb-workflow-ops-test");
|
|
74
|
+
const state = createInitialState(config);
|
|
75
|
+
const msft: TickerRecord = {
|
|
76
|
+
metadata: {
|
|
77
|
+
ticker: "MSFT",
|
|
78
|
+
exchange: "NASDAQ",
|
|
79
|
+
currency: "USD",
|
|
80
|
+
name: "Microsoft",
|
|
81
|
+
portfolios: [],
|
|
82
|
+
watchlists: [],
|
|
83
|
+
positions: [],
|
|
84
|
+
broker_contracts: [],
|
|
85
|
+
custom: {},
|
|
86
|
+
tags: [],
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
state.tickers.set("MSFT", msft);
|
|
90
|
+
let createdSymbol: string | null | undefined;
|
|
91
|
+
|
|
92
|
+
await createPaneTemplateOrThrow("ticker-pane", { symbol: "MSFT" }, {
|
|
93
|
+
dataProvider: makeDataProvider() as any,
|
|
94
|
+
tickerRepository: makeTickerRepository() as any,
|
|
95
|
+
dispatch: () => {},
|
|
96
|
+
getState: () => state,
|
|
97
|
+
pluginRegistry: {
|
|
98
|
+
paneTemplates: new Map([["ticker-pane", {
|
|
99
|
+
id: "ticker-pane",
|
|
100
|
+
paneId: "ticker-view",
|
|
101
|
+
label: "Ticker View",
|
|
102
|
+
description: "Ticker View",
|
|
103
|
+
shortcut: { prefix: "TV", argPlaceholder: "ticker", argKind: "ticker" },
|
|
104
|
+
createInstance: (_context: unknown, options: { symbol?: string } | undefined) => {
|
|
105
|
+
createdSymbol = options?.symbol;
|
|
106
|
+
return { binding: { kind: "fixed", symbol: options?.symbol ?? "" } };
|
|
107
|
+
},
|
|
108
|
+
}]]),
|
|
109
|
+
panes: new Map([["ticker-view", {
|
|
110
|
+
id: "ticker-view",
|
|
111
|
+
name: "Ticker View",
|
|
112
|
+
component: () => null,
|
|
113
|
+
defaultPosition: "right",
|
|
114
|
+
}]]),
|
|
115
|
+
getPaneTemplatePluginId: () => undefined,
|
|
116
|
+
events: { emit: () => {} },
|
|
117
|
+
} as any,
|
|
118
|
+
buildPaneInstance: () => ({ instanceId: "ticker-view:1", paneId: "ticker-view" }) as any,
|
|
119
|
+
placePaneInstance: () => {},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(createdSymbol).toBe("MSFT");
|
|
123
|
+
});
|
|
124
|
+
|
|
71
125
|
test("passes pane template instance ids through to pane creation", async () => {
|
|
72
126
|
const config = createDefaultConfig("/tmp/gloomberb-workflow-ops-test");
|
|
73
127
|
const state = createInitialState(config);
|
|
@@ -152,7 +152,7 @@ async function resolvePaneTemplateOptions(
|
|
|
152
152
|
let resolvedOptions = options;
|
|
153
153
|
if (template.shortcut?.argPlaceholder === "ticker") {
|
|
154
154
|
const resolvedTicker = await resolveTickerInputOrThrow(
|
|
155
|
-
resolvedOptions?.arg,
|
|
155
|
+
resolvedOptions?.symbol ?? resolvedOptions?.arg,
|
|
156
156
|
baseContext.activeTicker,
|
|
157
157
|
baseContext.activeCollectionId,
|
|
158
158
|
deps,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { expect, test } from "bun:test";
|
|
2
|
+
import { EXCEL_CSV_BOM } from "../../utils/csv";
|
|
3
|
+
import { createDataTableCsv } from "./export";
|
|
4
|
+
|
|
5
|
+
test("exports displayed table cells and skips section headers", () => {
|
|
6
|
+
const items = [
|
|
7
|
+
{ type: "section", name: "Americas", value: "" },
|
|
8
|
+
{ type: "row", name: "S&P 500", value: "+1.25%" },
|
|
9
|
+
];
|
|
10
|
+
const csv = createDataTableCsv({
|
|
11
|
+
columns: [
|
|
12
|
+
{ id: "name", label: "Name", width: 20, align: "left" },
|
|
13
|
+
{ id: "value", label: "Change", width: 10, align: "right" },
|
|
14
|
+
],
|
|
15
|
+
items,
|
|
16
|
+
renderSectionHeader: (item) => item.type === "section" ? { text: item.name } : null,
|
|
17
|
+
renderCell: (item, column) => ({ text: item[column.id as "name" | "value"] }),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
expect(csv).toBe(`${EXCEL_CSV_BOM}Name,Change\nS&P 500,'+1.25%`);
|
|
21
|
+
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { DataTableColumn, DataTableProps } from "../ui";
|
|
2
|
+
import { serializeCsv } from "../../utils/csv";
|
|
3
|
+
|
|
4
|
+
export function createDataTableCsv<
|
|
5
|
+
T,
|
|
6
|
+
C extends DataTableColumn = DataTableColumn,
|
|
7
|
+
>({
|
|
8
|
+
columns,
|
|
9
|
+
items,
|
|
10
|
+
renderCell,
|
|
11
|
+
renderSectionHeader,
|
|
12
|
+
}: Pick<DataTableProps<T, C>, "columns" | "items" | "renderCell" | "renderSectionHeader">): string {
|
|
13
|
+
const rows = items.flatMap((item, index) => {
|
|
14
|
+
if (renderSectionHeader?.(item, index)) return [];
|
|
15
|
+
return [columns.map((column) => renderCell(item, column, index, { selected: false }).text)];
|
|
16
|
+
});
|
|
17
|
+
return serializeCsv([
|
|
18
|
+
columns.map((column) => column.label),
|
|
19
|
+
...rows,
|
|
20
|
+
], { excelCompatible: true });
|
|
21
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { Box, Span, Text, useRendererHost, useUiCapabilities } from "../../ui";
|
|
1
|
+
import { Box, Span, Text, useContextMenu, useRendererHost, useUiCapabilities } from "../../ui";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { t } from "../../i18n";
|
|
4
4
|
import { useShortcut, useViewport } from "../../react/input";
|
|
5
|
-
import { useAppDispatch, useAppSelector } from "../../state/app/context";
|
|
5
|
+
import { resolveTickerForPane, useAppDispatch, useAppSelector } from "../../state/app/context";
|
|
6
6
|
import type { DesktopWindowBridge } from "../../types/desktop-window";
|
|
7
7
|
import { findPaneInstance } from "../../types/config";
|
|
8
8
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
@@ -20,6 +20,13 @@ import {
|
|
|
20
20
|
recordDoubleEscapeClose,
|
|
21
21
|
resetDoubleEscapeClose,
|
|
22
22
|
} from "../../utils/double-escape-close";
|
|
23
|
+
import { createShare, openLiveShareUrl } from "../../shares/api";
|
|
24
|
+
import { buildPaneSharePayload } from "../../shares/pane";
|
|
25
|
+
import type { ContextMenuItem } from "../../types/context-menu";
|
|
26
|
+
import {
|
|
27
|
+
PANE_MANAGEMENT_ACCELERATORS,
|
|
28
|
+
resolvePaneManagementShortcut,
|
|
29
|
+
} from "./shell/shortcuts";
|
|
23
30
|
|
|
24
31
|
interface DetachedPaneShellProps {
|
|
25
32
|
pluginRegistry: PluginRegistry;
|
|
@@ -35,6 +42,7 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
35
42
|
const colors = useThemeColors();
|
|
36
43
|
const dispatch = useAppDispatch();
|
|
37
44
|
const rendererHost = useRendererHost();
|
|
45
|
+
const { showContextMenu } = useContextMenu();
|
|
38
46
|
const config = useAppSelector((state) => state.config);
|
|
39
47
|
const paneState = useAppSelector((state) => state.paneState);
|
|
40
48
|
const inputCaptured = useAppSelector((state) => state.inputCaptured);
|
|
@@ -46,6 +54,7 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
46
54
|
const {
|
|
47
55
|
cellHeightPx = 18,
|
|
48
56
|
nativePaneChrome,
|
|
57
|
+
publicSharing,
|
|
49
58
|
titleBarOverlay,
|
|
50
59
|
nativeWindowChrome = titleBarOverlay,
|
|
51
60
|
windowControls,
|
|
@@ -55,13 +64,21 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
55
64
|
const instance = useAppSelector((state) => findPaneInstance(state.config.layout, desktopWindowBridge.paneId) ?? null);
|
|
56
65
|
const paneDef = instance ? pluginRegistry.panes.get(instance.paneId) ?? null : null;
|
|
57
66
|
const hasPaneSettings = !!instance && pluginRegistry.hasPaneSettings(instance.instanceId);
|
|
58
|
-
const quickSettings = instance ? pluginRegistry.resolvePaneQuickSettings(instance.instanceId) : [];
|
|
59
67
|
const titleState = useMemo(
|
|
60
|
-
() => ({ config, paneState }) as Parameters<typeof
|
|
68
|
+
() => ({ config, paneState }) as Parameters<typeof resolveTickerForPane>[0],
|
|
61
69
|
[config, paneState],
|
|
62
70
|
);
|
|
71
|
+
const sharePayload = useMemo(() => instance && publicSharing
|
|
72
|
+
? buildPaneSharePayload(
|
|
73
|
+
pluginRegistry,
|
|
74
|
+
instance,
|
|
75
|
+
paneState[instance.instanceId] ?? {},
|
|
76
|
+
resolveTickerForPane(titleState, instance.instanceId),
|
|
77
|
+
)
|
|
78
|
+
: null, [instance, paneState, pluginRegistry, publicSharing, titleState]);
|
|
79
|
+
const quickSettings = instance ? pluginRegistry.resolvePaneQuickSettings(instance.instanceId) : [];
|
|
63
80
|
const title = instance && paneDef
|
|
64
|
-
? getPaneDisplayTitle(titleState, instance, paneDef)
|
|
81
|
+
? getPaneDisplayTitle(titleState, instance, paneDef, pluginRegistry.panes)
|
|
65
82
|
: "Detached Pane";
|
|
66
83
|
const focused = windowFocused;
|
|
67
84
|
|
|
@@ -121,10 +138,56 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
121
138
|
if (nativeWindowChrome) void rendererHost.startWindowDrag?.();
|
|
122
139
|
}, [focusPane, nativeWindowChrome, rendererHost]);
|
|
123
140
|
|
|
124
|
-
const
|
|
141
|
+
const sharePane = useCallback(async () => {
|
|
142
|
+
if (!sharePayload) return;
|
|
143
|
+
try {
|
|
144
|
+
const { id } = await createShare(sharePayload);
|
|
145
|
+
await rendererHost.copyText(openLiveShareUrl(id));
|
|
146
|
+
pluginRegistry.notify({ body: "Share link copied to clipboard", type: "success" });
|
|
147
|
+
} catch (error) {
|
|
148
|
+
pluginRegistry.notify({
|
|
149
|
+
body: error instanceof Error ? error.message : "Could not share this pane.",
|
|
150
|
+
type: "error",
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}, [pluginRegistry, rendererHost, sharePayload]);
|
|
154
|
+
|
|
155
|
+
useShortcut((event) => {
|
|
156
|
+
if (resolvePaneManagementShortcut(event) !== "share" || !sharePayload) return;
|
|
157
|
+
if (inputCaptured && event.ctrl && !event.meta && !event.super) return;
|
|
158
|
+
event.preventDefault();
|
|
159
|
+
event.stopPropagation();
|
|
160
|
+
void sharePane();
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const openActions = useCallback((event?: { stopPropagation?: () => void; preventDefault?: () => void }) => {
|
|
125
164
|
stopMouse(event);
|
|
126
|
-
|
|
127
|
-
|
|
165
|
+
focusPane();
|
|
166
|
+
const items: ContextMenuItem[] = [];
|
|
167
|
+
if (hasPaneSettings) {
|
|
168
|
+
items.push({
|
|
169
|
+
id: "settings",
|
|
170
|
+
label: "Settings",
|
|
171
|
+
onSelect: () => pluginRegistry.openPaneSettingsFn(desktopWindowBridge.paneId),
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
if (sharePayload) items.push({
|
|
175
|
+
id: "share-pane",
|
|
176
|
+
label: "Share Pane",
|
|
177
|
+
accelerator: PANE_MANAGEMENT_ACCELERATORS.share,
|
|
178
|
+
onSelect: sharePane,
|
|
179
|
+
});
|
|
180
|
+
void showContextMenu({
|
|
181
|
+
kind: "pane",
|
|
182
|
+
paneId: desktopWindowBridge.paneId,
|
|
183
|
+
paneType: instance?.paneId ?? "",
|
|
184
|
+
title,
|
|
185
|
+
floating: true,
|
|
186
|
+
}, items, event).then((shown) => {
|
|
187
|
+
if (!shown && hasPaneSettings) pluginRegistry.openPaneSettingsFn(desktopWindowBridge.paneId);
|
|
188
|
+
else if (!shown && sharePayload) void sharePane();
|
|
189
|
+
});
|
|
190
|
+
}, [desktopWindowBridge.paneId, focusPane, hasPaneSettings, instance?.paneId, pluginRegistry, sharePane, sharePayload, showContextMenu, title]);
|
|
128
191
|
const toggleQuickSetting = useCallback((key: string, event?: { stopPropagation?: () => void; preventDefault?: () => void }) => {
|
|
129
192
|
stopMouse(event);
|
|
130
193
|
focusPane();
|
|
@@ -230,14 +293,15 @@ export function DetachedPaneShell({ pluginRegistry, desktopWindowBridge }: Detac
|
|
|
230
293
|
</Box>
|
|
231
294
|
))}
|
|
232
295
|
<Box flexGrow={1} minWidth={0} />
|
|
233
|
-
{hasPaneSettings && (
|
|
296
|
+
{(hasPaneSettings || sharePayload) && (
|
|
234
297
|
<Text
|
|
235
298
|
fg={paneTitleText(focused, true, colors)}
|
|
236
299
|
selectable={false}
|
|
237
300
|
className="electrobun-webkit-app-region-no-drag"
|
|
238
301
|
data-gloom-role="pane-action"
|
|
239
302
|
data-gloom-interactive="true"
|
|
240
|
-
|
|
303
|
+
aria-label="Pane actions"
|
|
304
|
+
onMouseDown={openActions}
|
|
241
305
|
>
|
|
242
306
|
{" ... "}
|
|
243
307
|
</Text>
|
|
@@ -1,9 +1,16 @@
|
|
|
1
1
|
import { resolveCollectionForPane, resolveTickerForPane, type AppState } from "../../../state/app/context";
|
|
2
|
-
import { t } from "../../../i18n";
|
|
3
|
-
import {
|
|
2
|
+
import { t, tf } from "../../../i18n";
|
|
3
|
+
import {
|
|
4
|
+
findPaneInstance,
|
|
5
|
+
TICKER_RESEARCH_PANE_ID,
|
|
6
|
+
type PaneInstanceConfig,
|
|
7
|
+
} from "../../../types/config";
|
|
4
8
|
import type { PaneDef } from "../../../types/plugin";
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
/** Single-cell link glyph: emoji link icons render double-width in terminals. */
|
|
11
|
+
const LINK_GLYPH = "\u29c9";
|
|
12
|
+
|
|
13
|
+
function getBasePaneDisplayTitle(
|
|
7
14
|
state: Pick<AppState, "config" | "paneState">,
|
|
8
15
|
instance: PaneInstanceConfig,
|
|
9
16
|
paneDef: PaneDef,
|
|
@@ -38,6 +45,27 @@ export function getPaneDisplayTitle(
|
|
|
38
45
|
?? t(paneDef.name);
|
|
39
46
|
}
|
|
40
47
|
|
|
48
|
+
// A source pane owns the cursor symbol; echoing it in its own title would just repeat the row.
|
|
49
|
+
if (paneDef.tickerSource) return t(paneDef.name);
|
|
50
|
+
|
|
41
51
|
const ticker = resolveTickerForPane(state as AppState, instance.instanceId);
|
|
42
52
|
return ticker ? `${t(paneDef.name)}: ${ticker}` : t(paneDef.name);
|
|
43
53
|
}
|
|
54
|
+
|
|
55
|
+
export function getPaneDisplayTitle(
|
|
56
|
+
state: Pick<AppState, "config" | "paneState">,
|
|
57
|
+
instance: PaneInstanceConfig,
|
|
58
|
+
paneDef: PaneDef,
|
|
59
|
+
panes?: ReadonlyMap<string, PaneDef>,
|
|
60
|
+
): string {
|
|
61
|
+
const title = getBasePaneDisplayTitle(state, instance, paneDef);
|
|
62
|
+
if (instance.paneId !== TICKER_RESEARCH_PANE_ID || instance.binding?.kind !== "follow" || !panes) {
|
|
63
|
+
return title;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const source = findPaneInstance(state.config.layout, instance.binding.sourceInstanceId);
|
|
67
|
+
const sourceDef = source ? panes.get(source.paneId) : null;
|
|
68
|
+
if (!source || !sourceDef) return title;
|
|
69
|
+
const sourceTitle = getBasePaneDisplayTitle(state, source, sourceDef);
|
|
70
|
+
return `${title} ${LINK_GLYPH} ${tf("Linked to {source}", { source: sourceTitle })}`;
|
|
71
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback
|
|
1
|
+
import { useCallback } from "react";
|
|
2
2
|
import {
|
|
3
3
|
floatAtRect,
|
|
4
4
|
getRememberedFloatingRect,
|
|
@@ -10,7 +10,6 @@ import {
|
|
|
10
10
|
type LayoutBounds,
|
|
11
11
|
type ResolvedPane,
|
|
12
12
|
} from "../../../plugins/pane-manager";
|
|
13
|
-
import type { AppAction } from "../../../state/app/context";
|
|
14
13
|
import type { LayoutConfig } from "../../../types/config";
|
|
15
14
|
import {
|
|
16
15
|
finalizePaneDragRelease,
|
|
@@ -32,7 +31,6 @@ interface UseShellActiveDragOptions {
|
|
|
32
31
|
appHeaderHeight: number;
|
|
33
32
|
bounds: LayoutBounds;
|
|
34
33
|
contentHeight: number;
|
|
35
|
-
dispatch: Dispatch<AppAction>;
|
|
36
34
|
dockGeometryOptions: DockGeometryOptions;
|
|
37
35
|
dockLeafLayouts: DockLeafLayout[];
|
|
38
36
|
dragRuntime: ShellDragRuntimeState;
|
|
@@ -52,7 +50,6 @@ export function useShellActiveDrag({
|
|
|
52
50
|
appHeaderHeight,
|
|
53
51
|
bounds,
|
|
54
52
|
contentHeight,
|
|
55
|
-
dispatch,
|
|
56
53
|
dockGeometryOptions,
|
|
57
54
|
dockLeafLayouts,
|
|
58
55
|
dragRuntime,
|
|
@@ -168,16 +165,13 @@ export function useShellActiveDrag({
|
|
|
168
165
|
? getRememberedFloatingRect(baseLayout, drag.paneId, width, contentHeight, pane?.def)
|
|
169
166
|
: drag.origRect;
|
|
170
167
|
const releaseRect = resolvePaneDragFloatingRect(drag, baseRect, preciseX, preciseShellY, width, contentHeight);
|
|
171
|
-
const
|
|
168
|
+
const nextLayout = finalizePaneDragRelease(baseLayout, drag.paneId, releaseRect, dockPreviewRef.current);
|
|
172
169
|
if (windowMode) {
|
|
173
|
-
updateWindowModePreviewLayout(
|
|
170
|
+
updateWindowModePreviewLayout(nextLayout, drag.paneId);
|
|
174
171
|
} else {
|
|
175
|
-
persistLayout(
|
|
172
|
+
persistLayout(nextLayout);
|
|
176
173
|
}
|
|
177
174
|
focusPane(drag.paneId);
|
|
178
|
-
if (!windowMode && releaseResult.shouldShowGridlockTip) {
|
|
179
|
-
dispatch({ type: "SHOW_GRIDLOCK_TIP" });
|
|
180
|
-
}
|
|
181
175
|
updateDockPreview(null);
|
|
182
176
|
setDragCursor(null);
|
|
183
177
|
updateDragFloatingRect(null);
|
|
@@ -201,7 +195,6 @@ export function useShellActiveDrag({
|
|
|
201
195
|
appHeaderHeight,
|
|
202
196
|
bounds,
|
|
203
197
|
contentHeight,
|
|
204
|
-
dispatch,
|
|
205
198
|
dockGeometryOptions,
|
|
206
199
|
dividerPreviewRef,
|
|
207
200
|
dockLeafLayouts,
|
|
@@ -46,11 +46,6 @@ export type DragPreview =
|
|
|
46
46
|
rect: FloatingRect;
|
|
47
47
|
};
|
|
48
48
|
|
|
49
|
-
export interface PaneDragReleaseResult {
|
|
50
|
-
nextLayout: LayoutConfig;
|
|
51
|
-
shouldShowGridlockTip: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
49
|
export interface PaneDragRectState {
|
|
55
50
|
mode: "docked" | "floating";
|
|
56
51
|
startX: number;
|
|
@@ -272,25 +267,14 @@ export function finalizePaneDragRelease(
|
|
|
272
267
|
paneId: string,
|
|
273
268
|
previewRect: FloatingRect,
|
|
274
269
|
dockPreview: DragPreview | null,
|
|
275
|
-
):
|
|
270
|
+
): LayoutConfig {
|
|
276
271
|
if (dockPreview?.kind === "dock") {
|
|
277
|
-
return
|
|
278
|
-
nextLayout: applyDrop(layout, paneId, dockPreview.target),
|
|
279
|
-
shouldShowGridlockTip: false,
|
|
280
|
-
};
|
|
272
|
+
return applyDrop(layout, paneId, dockPreview.target);
|
|
281
273
|
}
|
|
282
|
-
|
|
283
274
|
if (dockPreview?.kind === "snap") {
|
|
284
|
-
return
|
|
285
|
-
nextLayout: floatAtRect(layout, paneId, dockPreview.rect),
|
|
286
|
-
shouldShowGridlockTip: true,
|
|
287
|
-
};
|
|
275
|
+
return floatAtRect(layout, paneId, dockPreview.rect);
|
|
288
276
|
}
|
|
289
|
-
|
|
290
|
-
return {
|
|
291
|
-
nextLayout: floatAtRect(layout, paneId, previewRect),
|
|
292
|
-
shouldShowGridlockTip: false,
|
|
293
|
-
};
|
|
277
|
+
return floatAtRect(layout, paneId, previewRect);
|
|
294
278
|
}
|
|
295
279
|
|
|
296
280
|
export function makeSnapGuides(width: number, height: number): SnapGuide[] {
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
type LayoutBounds,
|
|
16
16
|
type ResolvedPane,
|
|
17
17
|
} from "../../../../plugins/pane-manager";
|
|
18
|
-
import type { AppAction } from "../../../../state/app/context";
|
|
19
18
|
import type { LayoutConfig } from "../../../../types/config";
|
|
20
19
|
import {
|
|
21
20
|
constrainFloatingRectToBounds,
|
|
@@ -165,7 +164,6 @@ interface UseShellPointerRuntimeOptions {
|
|
|
165
164
|
bounds: LayoutBounds;
|
|
166
165
|
closePaneMenu: () => void;
|
|
167
166
|
contentHeight: number;
|
|
168
|
-
dispatch: Dispatch<AppAction>;
|
|
169
167
|
dockGeometryOptions: DockGeometryOptions;
|
|
170
168
|
dockDividerLayouts: DockDividerLayout[];
|
|
171
169
|
dockLeafLayouts: DockLeafLayout[];
|
|
@@ -193,6 +191,7 @@ interface UseShellPointerRuntimeOptions {
|
|
|
193
191
|
visibleLayout: LayoutConfig;
|
|
194
192
|
width: number;
|
|
195
193
|
windowMode: WindowEditState | null;
|
|
194
|
+
commandBarOpen: boolean;
|
|
196
195
|
}
|
|
197
196
|
|
|
198
197
|
export function useShellPointerRuntime({
|
|
@@ -200,7 +199,6 @@ export function useShellPointerRuntime({
|
|
|
200
199
|
bounds,
|
|
201
200
|
closePaneMenu,
|
|
202
201
|
contentHeight,
|
|
203
|
-
dispatch,
|
|
204
202
|
dockGeometryOptions,
|
|
205
203
|
dockDividerLayouts,
|
|
206
204
|
dockLeafLayouts,
|
|
@@ -224,12 +222,12 @@ export function useShellPointerRuntime({
|
|
|
224
222
|
visibleLayout,
|
|
225
223
|
width,
|
|
226
224
|
windowMode,
|
|
225
|
+
commandBarOpen,
|
|
227
226
|
}: UseShellPointerRuntimeOptions) {
|
|
228
227
|
const handleActiveDrag = useShellActiveDrag({
|
|
229
228
|
appHeaderHeight,
|
|
230
229
|
bounds,
|
|
231
230
|
contentHeight,
|
|
232
|
-
dispatch,
|
|
233
231
|
dockGeometryOptions,
|
|
234
232
|
dockLeafLayouts,
|
|
235
233
|
focusPane,
|
|
@@ -282,6 +280,7 @@ export function useShellPointerRuntime({
|
|
|
282
280
|
setMenuState,
|
|
283
281
|
transientFocusActive,
|
|
284
282
|
windowMode,
|
|
283
|
+
commandBarOpen,
|
|
285
284
|
});
|
|
286
285
|
|
|
287
286
|
return {
|
|
@@ -62,6 +62,7 @@ function createShellPluginRegistry(options?: {
|
|
|
62
62
|
hasPaneSettings: (paneId: string) => paneId === "portfolio-list:main",
|
|
63
63
|
openPaneSettingsFn: () => {},
|
|
64
64
|
openCommandBar: () => {},
|
|
65
|
+
showPane: () => {},
|
|
65
66
|
openWindowMode: () => {},
|
|
66
67
|
openWindowModeFn: () => {},
|
|
67
68
|
updateLayoutFn: () => {},
|
|
@@ -356,7 +357,8 @@ describe("Shell", () => {
|
|
|
356
357
|
expect(resolvePaneManagementShortcut({ ...base, name: "D", key: "D" })).toBe("toggle-floating");
|
|
357
358
|
expect(resolvePaneManagementShortcut({ ...base, name: "o", key: "o" })).toBe("pop-out");
|
|
358
359
|
expect(resolvePaneManagementShortcut({ ...base, name: "c", key: "c" })).toBe("copy-screenshot");
|
|
359
|
-
expect(resolvePaneManagementShortcut({ ...base, name: "
|
|
360
|
+
expect(resolvePaneManagementShortcut({ ...base, name: "s", key: "s" })).toBe("share");
|
|
361
|
+
expect(resolvePaneManagementShortcut({ ...base, name: "l", key: "l" })).toBe("layout-gallery");
|
|
360
362
|
expect(resolvePaneManagementShortcut({ ...base, name: "f", key: "f" })).toBe("toggle-fullscreen");
|
|
361
363
|
expect(resolvePaneManagementShortcut({ ...base, name: "g", key: "g" })).toBe("gridlock-all");
|
|
362
364
|
expect(resolvePaneManagementShortcut({ ...base, name: "m", key: "m" })).toBe("window-mode");
|
|
@@ -366,6 +368,18 @@ describe("Shell", () => {
|
|
|
366
368
|
expect(resolvePaneManagementShortcut({ ...base, name: "d", key: "d", meta: false, super: false })).toBeNull();
|
|
367
369
|
});
|
|
368
370
|
|
|
371
|
+
test("opens the layout browser from the primary Shift-L shortcut", async () => {
|
|
372
|
+
const config = createDefaultConfig("/tmp/gloomberb-shell-layout-browser-shortcut-test");
|
|
373
|
+
const opened: string[] = [];
|
|
374
|
+
const registry = createShellPluginRegistry();
|
|
375
|
+
registry.showPane = (paneId) => opened.push(paneId);
|
|
376
|
+
await renderShellForWindowModeTest(createInitialState(config), { registry });
|
|
377
|
+
|
|
378
|
+
await emitKeypress({ name: "l", ctrl: true, shift: true });
|
|
379
|
+
|
|
380
|
+
expect(opened).toEqual(["layout-marketplace"]);
|
|
381
|
+
});
|
|
382
|
+
|
|
369
383
|
test("toggles the focused pane fullscreen without persisting layout", async () => {
|
|
370
384
|
const config = createDefaultConfig("/tmp/gloomberb-shell-fullscreen-shortcut-test");
|
|
371
385
|
const mainPane = requireLayoutInstance(config, "portfolio-list:main");
|
|
@@ -478,8 +492,8 @@ describe("Shell", () => {
|
|
|
478
492
|
|
|
479
493
|
const frame = testSetup!.captureCharFrame();
|
|
480
494
|
const rows = frame.split("\n");
|
|
481
|
-
expect(rows[2]?.indexOf("
|
|
482
|
-
expect(rows[5]?.indexOf("
|
|
495
|
+
expect(rows[2]?.indexOf(":: Main Portfolio") ?? -1).toBeLessThan(0);
|
|
496
|
+
expect(rows[5]?.indexOf(":: Main Portfolio")).toBeGreaterThanOrEqual(14);
|
|
483
497
|
|
|
484
498
|
await act(async () => {
|
|
485
499
|
await testSetup!.mockMouse.release(16, 6);
|