gloomberb 0.9.4 → 0.9.6
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 +49 -8
- package/package.json +4 -3
- package/patches/@opentui%2Fcore@0.3.2.patch +231 -29
- package/patches/react-reconciler@0.33.0.patch +10 -0
- package/src/app/global-shortcuts.test.tsx +20 -1
- package/src/app/global-shortcuts.ts +2 -0
- package/src/app/pane-runtime/index.tsx +11 -4
- package/src/app/pane-runtime/pane-settings-runtime.tsx +1 -1
- package/src/app.tsx +41 -17
- package/src/cli/commands/remote.ts +206 -0
- package/src/cli/entry.ts +8 -1
- package/src/cli/index.ts +2 -0
- package/src/cli/native-smoke.ts +5 -0
- package/src/components/chart/core/keyboard.ts +10 -1
- package/src/components/chart/native/bitmap-support.test.ts +27 -0
- package/src/components/chart/native/bitmap-support.ts +55 -0
- package/src/components/chart/native/kitty/index.test.ts +47 -0
- package/src/components/chart/native/surface/visibility.ts +8 -1
- package/src/components/chart/static/bar-chart-surface.test.tsx +4 -0
- package/src/components/chart/static/chart/bitmap.ts +16 -9
- package/src/components/chart/static/chart/surface.test.tsx +59 -4
- package/src/components/chart/static/chart/surface.tsx +4 -0
- package/src/components/chart/stock-chart/index.test.ts +7 -0
- package/src/components/command-bar/list/view.tsx +48 -1
- package/src/components/command-bar/multi-select-picker.tsx +7 -0
- package/src/components/command-bar/routing/navigation-state.ts +10 -1
- package/src/components/command-bar/surface/index.test.tsx +14 -0
- package/src/components/command-bar/theme-picker.tsx +91 -79
- package/src/components/command-bar/workflow/field-row.tsx +30 -5
- package/src/components/data-table/view.tsx +0 -12
- package/src/components/index.ts +7 -0
- package/src/components/layout/floating-pane.tsx +3 -3
- package/src/components/layout/pane/footer/index.tsx +23 -0
- package/src/components/layout/pane/header.tsx +5 -0
- package/src/components/layout/pane/index.tsx +3 -3
- package/src/components/layout/shell/action-menu-overlay.tsx +1 -1
- package/src/components/layout/shell/cursor-occlusion.ts +9 -1
- package/src/components/layout/shell/index.test.tsx +2 -0
- package/src/components/layout/shell/index.tsx +16 -4
- package/src/components/layout/shell/pane/actions.ts +21 -7
- package/src/components/layout/shell/pane/layers.tsx +2 -2
- package/src/components/layout/status-bar.tsx +5 -5
- package/src/components/metric-treemap/index.tsx +317 -0
- package/src/components/metric-treemap/layout.test.ts +77 -0
- package/src/components/metric-treemap/layout.ts +476 -0
- package/src/components/pane-settings-dialog/desktop.tsx +1 -1
- package/src/components/price-performance.tsx +1 -1
- package/src/components/price-sparkline/view.tsx +15 -14
- package/src/components/speedometer-gauge/terminal.tsx +18 -15
- package/src/components/table-view-shared.tsx +0 -10
- package/src/components/ticker/list-table-view.tsx +1 -7
- package/src/components/ticker/list-table.test.tsx +0 -4
- package/src/components/toggle-list.tsx +10 -0
- package/src/components/ui/button.tsx +12 -0
- package/src/components/ui/data-table/index.tsx +51 -0
- package/src/components/ui/data-table/opentui/index.tsx +4 -10
- package/src/components/ui/data-table/types.ts +6 -4
- package/src/components/ui/fields.tsx +18 -0
- package/src/components/ui/list-view.tsx +84 -4
- package/src/components/ui/message-composer.tsx +22 -0
- package/src/components/ui/tabs.tsx +46 -2
- package/src/components/ui/ui.test.tsx +0 -9
- package/src/core/state/app/layout-reducer.ts +7 -1
- package/src/core/state/app/layout.ts +6 -0
- package/src/core/state/app/state.ts +23 -1
- package/src/core/state/app/types.ts +4 -2
- package/src/plugins/builtin/account-management/form-components.tsx +2 -2
- package/src/plugins/builtin/alerts/pane.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +1 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +1 -1
- package/src/plugins/builtin/chat/channels.ts +5 -18
- package/src/plugins/builtin/chat/chat.test.tsx +1 -7
- package/src/plugins/builtin/chat/message/desktop.tsx +1 -1
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +1 -1
- package/src/plugins/builtin/chat/message/terminal.tsx +2 -2
- package/src/plugins/builtin/chat/sidebar.tsx +1 -1
- package/src/plugins/builtin/chat/status-widget.tsx +2 -8
- package/src/plugins/builtin/cloud/auth-actions.tsx +2 -2
- package/src/plugins/builtin/cloud/plugin.tsx +4 -13
- package/src/plugins/builtin/correlation/matrix/symbol-cell.tsx +1 -1
- package/src/plugins/builtin/debug/index.tsx +3 -4
- package/src/plugins/builtin/help/components.tsx +1 -1
- package/src/plugins/builtin/holders/treemap.tsx +28 -297
- package/src/plugins/builtin/holders/types.ts +0 -34
- package/src/plugins/builtin/market-heatmap/data.test.ts +121 -0
- package/src/plugins/builtin/market-heatmap/data.ts +296 -0
- package/src/plugins/builtin/market-heatmap/index.tsx +320 -0
- package/src/plugins/builtin/options/table.ts +5 -5
- package/src/plugins/builtin/plugin-groups.ts +2 -0
- package/src/plugins/builtin/portfolio-list/grid.tsx +197 -0
- package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +41 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +78 -22
- package/src/plugins/ibkr/gateway/service/index.test.ts +22 -0
- package/src/plugins/ibkr/gateway/service/index.ts +6 -9
- package/src/plugins/ibkr/gateway/service/native-loader.ts +12 -0
- package/src/plugins/ibkr/trade/ticket-panel.tsx +1 -1
- package/src/remote/app-host.tsx +77 -0
- package/src/remote/client.ts +34 -0
- package/src/remote/command-bar.ts +98 -0
- package/src/remote/controller-utils.ts +111 -0
- package/src/remote/controller.test.ts +365 -0
- package/src/remote/controller.ts +434 -0
- package/src/remote/endpoint.ts +41 -0
- package/src/remote/json-patch.test.ts +31 -0
- package/src/remote/json-patch.ts +89 -0
- package/src/remote/layout-helpers.ts +62 -0
- package/src/remote/resources.ts +186 -0
- package/src/remote/revision.ts +18 -0
- package/src/remote/schema.ts +143 -0
- package/src/remote/semantic-helpers.ts +161 -0
- package/src/remote/semantic-tree.tsx +112 -0
- package/src/remote/server.test.ts +50 -0
- package/src/remote/server.ts +121 -0
- package/src/remote/types.ts +119 -0
- package/src/renderers/electrobun/bun/index.ts +55 -1
- package/src/renderers/electrobun/shared/protocol.ts +11 -1
- package/src/renderers/electrobun/view/backend-rpc.ts +28 -1
- package/src/renderers/electrobun/view/build-assets.ts +1 -0
- package/src/renderers/electrobun/view/data-table/index.tsx +0 -8
- package/src/renderers/electrobun/view/data-table/row.tsx +2 -12
- package/src/renderers/electrobun/view/host/box.tsx +11 -1
- package/src/renderers/electrobun/view/host/mouse.test.ts +37 -0
- package/src/renderers/electrobun/view/host/mouse.ts +2 -0
- package/src/renderers/electrobun/view/host/style.ts +1 -0
- package/src/renderers/electrobun/view/main.tsx +5 -1
- package/src/renderers/electrobun/view/native-stubs/ibkr-native-loader.ts +3 -0
- package/src/renderers/electrobun/view/styles.css +7 -0
- package/src/renderers/opentui/chart-surface.test.tsx +101 -0
- package/src/renderers/opentui/chart-surface.tsx +15 -20
- package/src/renderers/opentui/native-smoke.ts +1 -0
- package/src/renderers/opentui/start.tsx +32 -0
- package/src/sources/yahoo-finance/http.ts +31 -4
- package/src/state/app/context/index.test.tsx +47 -0
- package/src/state/app/context/index.tsx +48 -18
- package/src/ui/host.tsx +1 -0
- package/src/ui/index.tsx +183 -7
- package/src/version.ts +1 -1
- package/src/plugins/builtin/holders/treemap-layout.ts +0 -231
package/README.md
CHANGED
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
# Gloomberb
|
|
6
6
|
|
|
7
|
-
**Open-source finance terminal. Fast, keyboard-driven and extensible.**
|
|
7
|
+
**Open-source finance terminal. Fast, keyboard-driven, and extensible.**
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Desktop app for macOS and Windows. Terminal UI for macOS, Linux, and Windows.
|
|
10
10
|
|
|
11
|
-
<a href="https://gloomberb.com/download/desktop"><strong>Download desktop
|
|
11
|
+
<a href="https://gloomberb.com/download/desktop"><strong>Download desktop</strong></a>
|
|
12
|
+
·
|
|
13
|
+
<a href="#install"><strong>Install the TUI</strong></a>
|
|
12
14
|
|
|
13
15
|
<br />
|
|
14
16
|
<br />
|
|
@@ -17,9 +19,22 @@ Available as a desktop app or TUI.
|
|
|
17
19
|
|
|
18
20
|
</div>
|
|
19
21
|
|
|
22
|
+
## Desktop App or TUI?
|
|
23
|
+
|
|
24
|
+
Gloomberb has two ways in:
|
|
25
|
+
|
|
26
|
+
| Surface | Best for | How it runs |
|
|
27
|
+
|---------|----------|-------------|
|
|
28
|
+
| Desktop app | A polished app window, pop-out panes, OS shortcuts, and built-in updates | Published for macOS and Windows. It also installs a `gloomberb` terminal command for the TUI. |
|
|
29
|
+
| Terminal UI | Fast keyboard workflows inside your terminal, SSH/dev boxes, Linux machines, and script-friendly setups | Runs with `gloomberb` on macOS, Linux, and Windows. |
|
|
30
|
+
|
|
31
|
+
Both share the same command language, plugin system, market data surfaces, portfolios, watchlists, alerts, notes, and AI tools.
|
|
32
|
+
|
|
20
33
|
## Install
|
|
21
34
|
|
|
22
|
-
macOS
|
|
35
|
+
### macOS
|
|
36
|
+
|
|
37
|
+
Install the desktop app and the `gloomberb` terminal command:
|
|
23
38
|
|
|
24
39
|
```bash
|
|
25
40
|
brew install --cask vincelwt/tap/gloomberb
|
|
@@ -27,13 +42,37 @@ brew install --cask vincelwt/tap/gloomberb
|
|
|
27
42
|
curl -fsSL gloomberb.com/install | bash
|
|
28
43
|
```
|
|
29
44
|
|
|
30
|
-
Both install `Gloomberb.app` and a `gloomberb`
|
|
45
|
+
Both install `Gloomberb.app` and a `gloomberb` command that runs the TUI through the app bundle, so the bundled runtime is stored once.
|
|
31
46
|
|
|
32
|
-
|
|
47
|
+
Prefer a direct download?
|
|
33
48
|
|
|
34
49
|
- [Download Gloomberb for Mac](https://gloomberb.com/download/desktop)
|
|
35
50
|
|
|
36
|
-
|
|
51
|
+
### Linux
|
|
52
|
+
|
|
53
|
+
Install the standalone TUI binary:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
curl -fsSL gloomberb.com/install | bash
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This installs `gloomberb` to `~/.local/bin` by default. A Linux desktop package is not published yet.
|
|
60
|
+
|
|
61
|
+
### Windows
|
|
62
|
+
|
|
63
|
+
Install the desktop app:
|
|
64
|
+
|
|
65
|
+
- [Download GloomberbSetup.exe for Windows x64](https://github.com/vincelwt/gloomberb/releases/latest/download/stable-win-x64-GloomberbSetup.exe)
|
|
66
|
+
|
|
67
|
+
The installer adds the app and a `gloomberb` terminal command. For a terminal-only setup, install Bun and use the package:
|
|
68
|
+
|
|
69
|
+
```powershell
|
|
70
|
+
bun install -g gloomberb
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Terminal Package
|
|
74
|
+
|
|
75
|
+
Already have Bun installed on any supported OS?
|
|
37
76
|
|
|
38
77
|
```bash
|
|
39
78
|
bun install -g gloomberb
|
|
@@ -45,7 +84,7 @@ Then run:
|
|
|
45
84
|
gloomberb
|
|
46
85
|
```
|
|
47
86
|
|
|
48
|
-
On macOS,
|
|
87
|
+
On macOS and Windows, desktop updates replace the installed app in place and keep the terminal command pointing at the updated runtime. Homebrew users can also update through `brew upgrade --cask gloomberb`.
|
|
49
88
|
|
|
50
89
|
For the best terminal experience, use a [Kitty](https://sw.kovidgoyal.net/kitty/)-compatible terminal such as Ghostty, Kitty, or WezTerm.
|
|
51
90
|
|
|
@@ -58,6 +97,7 @@ Open command mode with `Ctrl+P`, then type a command. Press `` ` `` to open tick
|
|
|
58
97
|
| `DES AAPL` | Security details |
|
|
59
98
|
| `GP NVDA` | Price chart |
|
|
60
99
|
| `TOP` | Ranked market stories |
|
|
100
|
+
| `HM` | Market heatmap |
|
|
61
101
|
| `MOST` | Market movers |
|
|
62
102
|
| `PF` | Portfolios and watchlists |
|
|
63
103
|
| `KELLY AAPL` | Position sizing |
|
|
@@ -177,6 +217,7 @@ Use `HELP` inside Gloomberb for the live shortcut list. The common command-bar p
|
|
|
177
217
|
| Shortcut | Function |
|
|
178
218
|
|----------|----------|
|
|
179
219
|
| `TOP` | Ranked market stories |
|
|
220
|
+
| `HM` | Market heatmap for large US stocks and ETFs |
|
|
180
221
|
| `MOST` | Top gainers, losers, most active, and trending tickers |
|
|
181
222
|
| `PM <query>` | Polymarket and Kalshi prediction data |
|
|
182
223
|
| `N` | News feed |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gloomberb",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.6",
|
|
4
4
|
"description": "Market research and portfolio tracker for the terminal",
|
|
5
5
|
"module": "src/index.tsx",
|
|
6
6
|
"type": "module",
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"build": "bun run scripts/build.ts",
|
|
19
19
|
"build:all": "bun run scripts/build.ts --all",
|
|
20
20
|
"desktop:view:build": "bun run scripts/build-electrobun-view.ts",
|
|
21
|
-
"desktop:dev": "electrobun
|
|
21
|
+
"desktop:dev": "bun run scripts/electrobun-dev.ts",
|
|
22
22
|
"desktop:build": "electrobun build",
|
|
23
23
|
"desktop:release": "electrobun build --env=stable",
|
|
24
24
|
"desktop:release:windows": "electrobun build --env=stable --targets=win-x64",
|
|
@@ -63,6 +63,7 @@
|
|
|
63
63
|
},
|
|
64
64
|
"patchedDependencies": {
|
|
65
65
|
"@opentui/core@0.3.2": "patches/@opentui%2Fcore@0.3.2.patch",
|
|
66
|
-
"electrobun@1.18.1": "patches/electrobun@1.18.1.patch"
|
|
66
|
+
"electrobun@1.18.1": "patches/electrobun@1.18.1.patch",
|
|
67
|
+
"react-reconciler@0.33.0": "patches/react-reconciler@0.33.0.patch"
|
|
67
68
|
}
|
|
68
69
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
diff --git a/index-218h9p3f.js b/index-218h9p3f.js
|
|
2
|
-
index b9445d2cca9477dfe06026da451b6d19268fcbdd..
|
|
2
|
+
index b9445d2cca9477dfe06026da451b6d19268fcbdd..da790c31da185325405227329dad2ac12f5329e7 100644
|
|
3
3
|
--- a/index-218h9p3f.js
|
|
4
4
|
+++ b/index-218h9p3f.js
|
|
5
5
|
@@ -6487,17 +6487,27 @@ class MouseParser {
|
|
@@ -210,7 +210,101 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
210
210
|
}
|
|
211
211
|
throw new Error(`opentui is not supported on the current platform: ${process.platform}-${process.arch}`);
|
|
212
212
|
}
|
|
213
|
-
@@ -
|
|
213
|
+
@@ -17996,5 +18031,7 @@ function isGapType(value) {
|
|
214
|
+
class BoxRenderable extends Renderable {
|
|
215
|
+
_backgroundColor;
|
|
216
|
+
+ _hoverBackgroundColor;
|
|
217
|
+
+ _isHovered = false;
|
|
218
|
+
_border;
|
|
219
|
+
_borderStyle;
|
|
220
|
+
_borderColor;
|
|
221
|
+
@@ -18023,6 +18060,7 @@ class BoxRenderable extends Renderable {
|
|
222
|
+
this._focusable = true;
|
|
223
|
+
}
|
|
224
|
+
this._backgroundColor = parseColor(options.backgroundColor || this._defaultOptions.backgroundColor);
|
|
225
|
+
+ this._hoverBackgroundColor = options.hoverBackgroundColor === undefined ? undefined : parseColor(options.hoverBackgroundColor);
|
|
226
|
+
this._border = options.border ?? this._defaultOptions.border;
|
|
227
|
+
if (!options.border && (options.borderStyle || options.borderColor || options.focusedBorderColor || options.customBorderChars)) {
|
|
228
|
+
this._border = true;
|
|
229
|
+
@@ -18064,11 +18102,26 @@ class BoxRenderable extends Renderable {
|
|
230
|
+
}
|
|
231
|
+
set backgroundColor(value) {
|
|
232
|
+
const newColor = parseColor(value ?? this._defaultOptions.backgroundColor);
|
|
233
|
+
- if (this._backgroundColor !== newColor) {
|
|
234
|
+
+ if (!this._backgroundColor.equals(newColor)) {
|
|
235
|
+
this._backgroundColor = newColor;
|
|
236
|
+
this.requestRender();
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
+ get hoverBackgroundColor() {
|
|
240
|
+
+ return this._hoverBackgroundColor;
|
|
241
|
+
+ }
|
|
242
|
+
+ set hoverBackgroundColor(value) {
|
|
243
|
+
+ const newColor = value === undefined || value === null ? undefined : parseColor(value);
|
|
244
|
+
+ const changed = newColor === undefined
|
|
245
|
+
+ ? this._hoverBackgroundColor !== undefined
|
|
246
|
+
+ : !this._hoverBackgroundColor?.equals(newColor);
|
|
247
|
+
+ if (changed) {
|
|
248
|
+
+ this._hoverBackgroundColor = newColor;
|
|
249
|
+
+ if (this._isHovered) {
|
|
250
|
+
+ this.requestRender();
|
|
251
|
+
+ }
|
|
252
|
+
+ }
|
|
253
|
+
+ }
|
|
254
|
+
get border() {
|
|
255
|
+
return this._border;
|
|
256
|
+
}
|
|
257
|
+
@@ -18152,9 +18205,39 @@ class BoxRenderable extends Renderable {
|
|
258
|
+
this.requestRender();
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
+ onMouseEvent(event) {
|
|
262
|
+
+ if (!this._hoverBackgroundColor) {
|
|
263
|
+
+ if (this._isHovered) {
|
|
264
|
+
+ this._isHovered = false;
|
|
265
|
+
+ this.requestRender();
|
|
266
|
+
+ }
|
|
267
|
+
+ return;
|
|
268
|
+
+ }
|
|
269
|
+
+ if (event.type === "over" && !this._isHovered) {
|
|
270
|
+
+ this._isHovered = true;
|
|
271
|
+
+ this.requestRender();
|
|
272
|
+
+ } else if (event.type === "out" && this._isHovered) {
|
|
273
|
+
+ this._isHovered = false;
|
|
274
|
+
+ this.requestRender();
|
|
275
|
+
+ }
|
|
276
|
+
+ }
|
|
277
|
+
+ getActiveHoverBackgroundColor() {
|
|
278
|
+
+ if (this._isHovered && this._hoverBackgroundColor) {
|
|
279
|
+
+ return this._hoverBackgroundColor;
|
|
280
|
+
+ }
|
|
281
|
+
+ let parent = this.parent;
|
|
282
|
+
+ while (parent) {
|
|
283
|
+
+ if (parent instanceof BoxRenderable && parent._isHovered && parent._hoverBackgroundColor) {
|
|
284
|
+
+ return parent._hoverBackgroundColor;
|
|
285
|
+
+ }
|
|
286
|
+
+ parent = parent.parent;
|
|
287
|
+
+ }
|
|
288
|
+
+ return undefined;
|
|
289
|
+
+ }
|
|
290
|
+
renderSelf(buffer) {
|
|
291
|
+
const hasBorder = this.borderSides.top || this.borderSides.right || this.borderSides.bottom || this.borderSides.left;
|
|
292
|
+
- const hasVisibleFill = this.shouldFill && this._backgroundColor.a > 0;
|
|
293
|
+
+ const backgroundColor = this.getActiveHoverBackgroundColor() ?? this._backgroundColor;
|
|
294
|
+
+ const hasVisibleFill = this.shouldFill && backgroundColor.a > 0;
|
|
295
|
+
if (!hasBorder && !hasVisibleFill) {
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
@@ -18171,7 +18254,7 @@ class BoxRenderable extends Renderable {
|
|
299
|
+
customBorderChars: this._customBorderChars,
|
|
300
|
+
border: this._border,
|
|
301
|
+
borderColor: currentBorderColor,
|
|
302
|
+
- backgroundColor: this._backgroundColor,
|
|
303
|
+
+ backgroundColor,
|
|
304
|
+
shouldFill: this.shouldFill,
|
|
305
|
+
title: this._title,
|
|
306
|
+
titleAlignment: this._titleAlignment,
|
|
307
|
+
@@ -22288,6 +22371,8 @@ class MouseEvent {
|
|
214
308
|
button;
|
|
215
309
|
x;
|
|
216
310
|
y;
|
|
@@ -219,7 +313,7 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
219
313
|
source;
|
|
220
314
|
modifiers;
|
|
221
315
|
scroll;
|
|
222
|
-
@@ -22307,6 +
|
|
316
|
+
@@ -22307,6 +22392,8 @@ class MouseEvent {
|
|
223
317
|
this.button = attributes.button;
|
|
224
318
|
this.x = attributes.x;
|
|
225
319
|
this.y = attributes.y;
|
|
@@ -228,16 +322,18 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
228
322
|
this.modifiers = attributes.modifiers;
|
|
229
323
|
this.scroll = attributes.scroll;
|
|
230
324
|
this.source = attributes.source;
|
|
231
|
-
@@ -22457,6 +
|
|
325
|
+
@@ -22457,6 +22544,10 @@ class CliRenderer extends EventEmitter9 {
|
|
232
326
|
resizeDebounceDelay = 100;
|
|
233
327
|
enableMouseMovement = false;
|
|
234
328
|
_useMouse = true;
|
|
235
329
|
+ _mouseProtocolSignature = "disabled";
|
|
236
330
|
+ _pixelMouseEnabled = false;
|
|
331
|
+
+ _pendingMouseMoveEvent = null;
|
|
332
|
+
+ _pendingMouseMoveTimer = null;
|
|
237
333
|
autoFocus = true;
|
|
238
334
|
_screenMode = "alternate-screen";
|
|
239
335
|
_footerHeight = DEFAULT_FOOTER_HEIGHT;
|
|
240
|
-
@@ -22491,7 +
|
|
336
|
+
@@ -22491,7 +22582,7 @@ class CliRenderer extends EventEmitter9 {
|
|
241
337
|
this.handleResize(width, height);
|
|
242
338
|
}).bind(this);
|
|
243
339
|
_capabilities = null;
|
|
@@ -246,7 +342,7 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
246
342
|
_hasPointer = false;
|
|
247
343
|
_lastPointerModifiers = {
|
|
248
344
|
shift: false,
|
|
249
|
-
@@ -22738,7 +
|
|
345
|
+
@@ -22738,7 +22829,13 @@ Captured external output:
|
|
250
346
|
privateCapabilityRepliesActive: false,
|
|
251
347
|
pixelResolutionQueryActive: false,
|
|
252
348
|
explicitWidthCprActive: false,
|
|
@@ -261,7 +357,7 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
261
357
|
},
|
|
262
358
|
clock: this.clock
|
|
263
359
|
});
|
|
264
|
-
@@ -24021,12 +
|
|
360
|
+
@@ -24021,12 +24118,14 @@ Captured external output:
|
|
265
361
|
enableMouse() {
|
|
266
362
|
this._useMouse = true;
|
|
267
363
|
this.lib.enableMouse(this.rendererPtr, this.enableMouseMovement);
|
|
@@ -276,7 +372,7 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
276
372
|
}
|
|
277
373
|
enableKittyKeyboard(flags = 3) {
|
|
278
374
|
this.lib.enableKittyKeyboard(this.rendererPtr, flags);
|
|
279
|
-
@@ -24036,6 +
|
|
375
|
+
@@ -24036,6 +24135,52 @@ Captured external output:
|
|
280
376
|
this.lib.disableKittyKeyboard(this.rendererPtr);
|
|
281
377
|
this.updateStdinParserProtocolContext({ kittyKeyboardEnabled: false }, true);
|
|
282
378
|
}
|
|
@@ -329,7 +425,7 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
329
425
|
set useThread(useThread) {
|
|
330
426
|
this._useThread = useThread;
|
|
331
427
|
this.lib.setUseThread(this.rendererPtr, useThread);
|
|
332
|
-
@@ -24141,6 +
|
|
428
|
+
@@ -24141,6 +24286,7 @@ Captured external output:
|
|
333
429
|
}
|
|
334
430
|
this.lib.processCapabilityResponse(this.rendererPtr, sequence);
|
|
335
431
|
this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
|
|
@@ -337,7 +433,7 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
337
433
|
if (this._capabilities?.terminal?.from_xtversion) {
|
|
338
434
|
this.resolveXtVersionWaiters();
|
|
339
435
|
}
|
|
340
|
-
@@ -24170,6 +
|
|
436
|
+
@@ -24170,6 +24316,7 @@ Captured external output:
|
|
341
437
|
if (sequence === "\x1B[I") {
|
|
342
438
|
if (this.shouldRestoreModesOnNextFocus) {
|
|
343
439
|
this.lib.restoreTerminalModes(this.rendererPtr);
|
|
@@ -345,7 +441,16 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
345
441
|
this.shouldRestoreModesOnNextFocus = false;
|
|
346
442
|
}
|
|
347
443
|
if (this._terminalFocusState !== true) {
|
|
348
|
-
@@ -
|
|
444
|
+
@@ -24218,7 +24365,7 @@ Captured external output:
|
|
445
|
+
this._keyHandler.processParsedKey(event.key);
|
|
446
|
+
return;
|
|
447
|
+
case "mouse":
|
|
448
|
+
- if (this._useMouse && this.processSingleMouseEvent(event.event)) {
|
|
449
|
+
+ if (this._useMouse && this.processMouseEvent(event.event)) {
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
this.dispatchSequenceHandlers(event.raw);
|
|
453
|
+
@@ -24264,6 +24411,7 @@ Captured external output:
|
|
349
454
|
}
|
|
350
455
|
this.waitingForPixelResolution = false;
|
|
351
456
|
this.updateStdinParserProtocolContext({ pixelResolutionQueryActive: false }, true);
|
|
@@ -353,7 +458,54 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
353
458
|
return true;
|
|
354
459
|
}
|
|
355
460
|
return false;
|
|
356
|
-
@@ -
|
|
461
|
+
@@ -24292,15 +24440,62 @@ Captured external output:
|
|
462
|
+
}
|
|
463
|
+
return event;
|
|
464
|
+
}
|
|
465
|
+
+ cloneMouseEventAttributes(mouseEvent) {
|
|
466
|
+
+ return {
|
|
467
|
+
+ ...mouseEvent,
|
|
468
|
+
+ modifiers: { ...mouseEvent.modifiers }
|
|
469
|
+
+ };
|
|
470
|
+
+ }
|
|
471
|
+
+ processMouseEvent(mouseEvent) {
|
|
472
|
+
+ if (mouseEvent.type !== "move") {
|
|
473
|
+
+ this.flushPendingMouseMove();
|
|
474
|
+
+ return this.processSingleMouseEvent(mouseEvent);
|
|
475
|
+
+ }
|
|
476
|
+
+ if (this._pendingMouseMoveTimer === null) {
|
|
477
|
+
+ const handled = this.processSingleMouseEvent(mouseEvent);
|
|
478
|
+
+ this.schedulePendingMouseMove();
|
|
479
|
+
+ return handled;
|
|
480
|
+
+ }
|
|
481
|
+
+ this._pendingMouseMoveEvent = this.cloneMouseEventAttributes(mouseEvent);
|
|
482
|
+
+ return true;
|
|
483
|
+
+ }
|
|
484
|
+
+ schedulePendingMouseMove() {
|
|
485
|
+
+ if (this._pendingMouseMoveTimer !== null || this._isDestroyed) {
|
|
486
|
+
+ return;
|
|
487
|
+
+ }
|
|
488
|
+
+ const delay = Math.max(100, Math.floor(this.targetFrameTime));
|
|
489
|
+
+ this._pendingMouseMoveTimer = this.clock.setTimeout(() => {
|
|
490
|
+
+ this._pendingMouseMoveTimer = null;
|
|
491
|
+
+ this.flushPendingMouseMove();
|
|
492
|
+
+ }, delay);
|
|
493
|
+
+ }
|
|
494
|
+
+ flushPendingMouseMove() {
|
|
495
|
+
+ if (this._pendingMouseMoveTimer !== null) {
|
|
496
|
+
+ this.clock.clearTimeout(this._pendingMouseMoveTimer);
|
|
497
|
+
+ this._pendingMouseMoveTimer = null;
|
|
498
|
+
+ }
|
|
499
|
+
+ const pending = this._pendingMouseMoveEvent;
|
|
500
|
+
+ this._pendingMouseMoveEvent = null;
|
|
501
|
+
+ if (!pending || this._isDestroyed) {
|
|
502
|
+
+ return false;
|
|
503
|
+
+ }
|
|
504
|
+
+ return this.processSingleMouseEvent(pending);
|
|
505
|
+
+ }
|
|
506
|
+
processSingleMouseEvent(mouseEvent) {
|
|
507
|
+
if (this._splitHeight > 0) {
|
|
508
|
+
if (mouseEvent.y < this.renderOffset) {
|
|
357
509
|
return false;
|
|
358
510
|
}
|
|
359
511
|
mouseEvent.y -= this.renderOffset;
|
|
@@ -369,31 +521,43 @@ index b9445d2cca9477dfe06026da451b6d19268fcbdd..3efd05fa41eb713ac6e539764b15c346
|
|
|
369
521
|
this._hasPointer = true;
|
|
370
522
|
this._lastPointerModifiers = mouseEvent.modifiers;
|
|
371
523
|
if (this._console.visible) {
|
|
372
|
-
@@ -
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
524
|
+
@@ -24437,6 +24632,8 @@ Captured external output:
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
const baseEvent = {
|
|
376
528
|
+ pixelX: this._latestPointer.pixelX,
|
|
377
529
|
+ pixelY: this._latestPointer.pixelY,
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
@@ -
|
|
530
|
+
type: "move",
|
|
531
|
+
button: 0,
|
|
532
|
+
x: this._latestPointer.x,
|
|
533
|
+
@@ -24527,6 +24724,7 @@ Captured external output:
|
|
534
|
+
const pendingSplitFooterTransition = this.pendingSplitFooterTransition;
|
|
535
|
+
const previousGeometry = calculateRenderGeometry(this._screenMode, this._terminalWidth, this._terminalHeight, this._footerHeight);
|
|
536
|
+
const prevWidth = this._terminalWidth;
|
|
537
|
+
+ this.syncPixelMouseMode();
|
|
538
|
+
const previousTerminalHeight = this._terminalHeight;
|
|
382
539
|
const visiblePreviousSplitHeight = pendingSplitFooterTransition?.sourceHeight ?? previousGeometry.effectiveFooterHeight;
|
|
383
540
|
this._terminalWidth = width;
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
541
|
+
@@ -24832,6 +25030,11 @@ Captured external output:
|
|
542
|
+
this.clock.clearTimeout(this.resizeTimeoutId);
|
|
543
|
+
this.resizeTimeoutId = null;
|
|
544
|
+
}
|
|
545
|
+
+ if (this._pendingMouseMoveTimer !== null) {
|
|
546
|
+
+ this.clock.clearTimeout(this._pendingMouseMoveTimer);
|
|
547
|
+
+ this._pendingMouseMoveTimer = null;
|
|
548
|
+
+ }
|
|
549
|
+
+ this._pendingMouseMoveEvent = null;
|
|
550
|
+
if (this.capabilityTimeoutId !== null) {
|
|
551
|
+
this.clock.clearTimeout(this.capabilityTimeoutId);
|
|
552
|
+
this.capabilityTimeoutId = null;
|
|
553
|
+
@@ -24847,6 +25050,7 @@ Captured external output:
|
|
554
|
+
this.renderTimeout = null;
|
|
555
|
+
}
|
|
390
556
|
this.themeModeState.cancelRefresh();
|
|
557
|
+
+ this.disablePixelMouseMode();
|
|
391
558
|
this._isRunning = false;
|
|
392
559
|
this.waitingForPixelResolution = false;
|
|
393
|
-
+ this.disablePixelMouseMode();
|
|
394
560
|
this.updateStdinParserProtocolContext({
|
|
395
|
-
privateCapabilityRepliesActive: false,
|
|
396
|
-
pixelResolutionQueryActive: false,
|
|
397
561
|
diff --git a/lib/parse.mouse.d.ts b/lib/parse.mouse.d.ts
|
|
398
562
|
index c29f953fde0b78f09d2ec7a830e5a72452c1fe7e..04613b921bd9b4a9a7a1a4fbd0234d4012df67a1 100644
|
|
399
563
|
--- a/lib/parse.mouse.d.ts
|
|
@@ -450,6 +614,44 @@ index 6b733d534d8e9b77c5c2773f93c7a22063c18d84..a5c318d591e9f3775e276e555c15052c
|
|
|
450
614
|
}
|
|
451
615
|
export interface StdinParserOptions {
|
|
452
616
|
timeoutMs?: number;
|
|
617
|
+
diff --git a/renderables/Box.d.ts b/renderables/Box.d.ts
|
|
618
|
+
index 7ecb70d38ca41d1afdc078780985827e8f10e3a7..0845141e20ebd71a163dba67880d494a05b27d9a 100644
|
|
619
|
+
--- a/renderables/Box.d.ts
|
|
620
|
+
+++ b/renderables/Box.d.ts
|
|
621
|
+
@@ -5,6 +5,7 @@ import { type ColorInput, RGBA } from "../lib/RGBA.js";
|
|
622
|
+
import type { RenderContext } from "../types.js";
|
|
623
|
+
export interface BoxOptions<TRenderable extends Renderable = BoxRenderable> extends RenderableOptions<TRenderable> {
|
|
624
|
+
backgroundColor?: string | RGBA;
|
|
625
|
+
+ hoverBackgroundColor?: string | RGBA;
|
|
626
|
+
borderStyle?: BorderStyle;
|
|
627
|
+
border?: boolean | BorderSides[];
|
|
628
|
+
borderColor?: string | RGBA;
|
|
629
|
+
@@ -22,6 +23,8 @@ export interface BoxOptions<TRenderable extends Renderable = BoxRenderable> exte
|
|
630
|
+
}
|
|
631
|
+
export declare class BoxRenderable extends Renderable {
|
|
632
|
+
protected _backgroundColor: RGBA;
|
|
633
|
+
+ protected _hoverBackgroundColor?: RGBA;
|
|
634
|
+
+ protected _isHovered: boolean;
|
|
635
|
+
protected _border: boolean | BorderSides[];
|
|
636
|
+
protected _borderStyle: BorderStyle;
|
|
637
|
+
protected _borderColor: RGBA;
|
|
638
|
+
@@ -50,6 +53,8 @@ export declare class BoxRenderable extends Renderable {
|
|
639
|
+
set customBorderChars(value: BorderCharacters | undefined);
|
|
640
|
+
get backgroundColor(): RGBA;
|
|
641
|
+
set backgroundColor(value: RGBA | string | undefined);
|
|
642
|
+
+ get hoverBackgroundColor(): RGBA | undefined;
|
|
643
|
+
+ set hoverBackgroundColor(value: RGBA | string | undefined);
|
|
644
|
+
get border(): boolean | BorderSides[];
|
|
645
|
+
set border(value: boolean | BorderSides[]);
|
|
646
|
+
get borderStyle(): BorderStyle;
|
|
647
|
+
@@ -66,6 +71,7 @@ export declare class BoxRenderable extends Renderable {
|
|
648
|
+
set bottomTitle(value: string | undefined);
|
|
649
|
+
get bottomTitleAlignment(): "left" | "center" | "right";
|
|
650
|
+
set bottomTitleAlignment(value: "left" | "center" | "right");
|
|
651
|
+
+ protected getActiveHoverBackgroundColor(): RGBA | undefined;
|
|
652
|
+
protected renderSelf(buffer: OptimizedBuffer): void;
|
|
653
|
+
protected getScissorRect(): {
|
|
654
|
+
x: number;
|
|
453
655
|
diff --git a/renderer.d.ts b/renderer.d.ts
|
|
454
656
|
index 96189246f423809fc52d2ad182d57ed2e80ac098..7dd0baf1697041cf8ad556b115cd13ad2e18bc65 100644
|
|
455
657
|
--- a/renderer.d.ts
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
diff --git a/cjs/react-reconciler.development.js b/cjs/react-reconciler.development.js
|
|
2
|
+
--- a/cjs/react-reconciler.development.js
|
|
3
|
+
+++ b/cjs/react-reconciler.development.js
|
|
4
|
+
@@ -17213,5 +17213,6 @@
|
|
5
|
+
hasOwnProperty = Object.prototype.hasOwnProperty,
|
|
6
|
+
supportsUserTiming =
|
|
7
|
+
+ "undefined" !== typeof window &&
|
|
8
|
+
"undefined" !== typeof console &&
|
|
9
|
+
"function" === typeof console.timeStamp &&
|
|
10
|
+
"undefined" !== typeof performance &&
|
|
@@ -2,7 +2,7 @@ import { afterEach, describe, expect, test } from "bun:test";
|
|
|
2
2
|
import { createTestRenderer } from "@opentui/core/testing";
|
|
3
3
|
import { act } from "react";
|
|
4
4
|
import { createOpenTuiTestRoot as createRoot, TestDialogProvider } from "../renderers/opentui/test-utils";
|
|
5
|
-
import { createDefaultConfig } from "../types/config";
|
|
5
|
+
import { cloneLayout, createDefaultConfig } from "../types/config";
|
|
6
6
|
import { createInitialState, type AppAction, type AppState } from "../state/app/context";
|
|
7
7
|
import type { PluginRegistry } from "../plugins/registry";
|
|
8
8
|
import { useAppGlobalShortcuts } from "./global-shortcuts";
|
|
@@ -164,6 +164,25 @@ describe("useAppGlobalShortcuts", () => {
|
|
|
164
164
|
expect(event.propagationStopped).toBe(true);
|
|
165
165
|
});
|
|
166
166
|
|
|
167
|
+
test("switches saved layouts with Ctrl-number and consumes the shortcut", async () => {
|
|
168
|
+
const actions: AppAction[] = [];
|
|
169
|
+
const config = createDefaultConfig("/tmp/gloomberb-global-shortcuts-layouts");
|
|
170
|
+
config.layouts = [
|
|
171
|
+
{ name: "One", layout: cloneLayout(config.layout) },
|
|
172
|
+
{ name: "Two", layout: cloneLayout(config.layout) },
|
|
173
|
+
{ name: "Three", layout: cloneLayout(config.layout) },
|
|
174
|
+
];
|
|
175
|
+
config.activeLayoutIndex = 0;
|
|
176
|
+
const state = createInitialState(config);
|
|
177
|
+
await renderHarness(state, createRegistry(), (action) => actions.push(action));
|
|
178
|
+
|
|
179
|
+
const event = await emitKeypress({ name: "2", ctrl: true });
|
|
180
|
+
|
|
181
|
+
expect(actions).toEqual([{ type: "SWITCH_LAYOUT", index: 1 }]);
|
|
182
|
+
expect(event.defaultPrevented).toBe(true);
|
|
183
|
+
expect(event.propagationStopped).toBe(true);
|
|
184
|
+
});
|
|
185
|
+
|
|
167
186
|
test("does not run plain plugin shortcuts while input is captured", async () => {
|
|
168
187
|
let executed = 0;
|
|
169
188
|
const actions: AppAction[] = [];
|
|
@@ -73,23 +73,30 @@ export function useAppPaneRuntime({
|
|
|
73
73
|
return resolvePaneTargetInLayout(layout, paneId);
|
|
74
74
|
}, [state.config.layout]);
|
|
75
75
|
|
|
76
|
-
const persistLayout = useCallback((layout: LayoutConfig, options?: { pushHistory?: boolean }) => {
|
|
76
|
+
const persistLayout = useCallback((layout: LayoutConfig, options?: { pushHistory?: boolean; focusedPaneId?: string | null }) => {
|
|
77
77
|
const currentState = stateRef.current;
|
|
78
78
|
const normalizedLayout = normalizePaneLayout(layout);
|
|
79
79
|
if (options?.pushHistory !== false) {
|
|
80
80
|
dispatch({ type: "PUSH_LAYOUT_HISTORY" });
|
|
81
81
|
}
|
|
82
|
-
|
|
82
|
+
const hasFocusTarget = !!options && Object.prototype.hasOwnProperty.call(options, "focusedPaneId");
|
|
83
|
+
dispatch(hasFocusTarget
|
|
84
|
+
? { type: "UPDATE_LAYOUT", layout: normalizedLayout, focusedPaneId: options.focusedPaneId ?? null }
|
|
85
|
+
: { type: "UPDATE_LAYOUT", layout: normalizedLayout });
|
|
83
86
|
scheduleConfigSave(syncConfigActiveLayoutState(
|
|
84
87
|
{ ...currentState.config, layout: normalizedLayout },
|
|
85
88
|
currentState.paneState,
|
|
86
|
-
currentState.focusedPaneId,
|
|
89
|
+
hasFocusTarget ? (options.focusedPaneId ?? null) : currentState.focusedPaneId,
|
|
87
90
|
currentState.activePanel,
|
|
88
91
|
));
|
|
89
92
|
}, [dispatch, stateRef]);
|
|
90
93
|
|
|
91
94
|
const activatePane = useCallback((paneId: string, layout: LayoutConfig = state.config.layout) => {
|
|
92
|
-
dispatch({
|
|
95
|
+
dispatch({
|
|
96
|
+
type: "SET_ACTIVE_PANEL",
|
|
97
|
+
panel: resolvePanelForPane({ layout, paneId, pluginRegistry }),
|
|
98
|
+
preserveFocus: true,
|
|
99
|
+
});
|
|
93
100
|
dispatch({ type: "FOCUS_PANE", paneId });
|
|
94
101
|
}, [dispatch, pluginRegistry, state.config.layout]);
|
|
95
102
|
|
|
@@ -15,7 +15,7 @@ interface UseAppPaneSettingsRuntimeOptions {
|
|
|
15
15
|
dataProvider: DataProvider;
|
|
16
16
|
dialog: DialogApi;
|
|
17
17
|
dispatch: Dispatch<AppAction>;
|
|
18
|
-
persistLayout: (layout: LayoutConfig, options?: { pushHistory?: boolean }) => void;
|
|
18
|
+
persistLayout: (layout: LayoutConfig, options?: { pushHistory?: boolean; focusedPaneId?: string | null }) => void;
|
|
19
19
|
pluginRegistry: PluginRegistry;
|
|
20
20
|
resolvePaneTarget: (paneId: string, layout?: LayoutConfig) => string | null;
|
|
21
21
|
stateRef: { current: AppState };
|