gloomberb 0.6.0 → 0.7.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/package.json +2 -1
- package/patches/@opentui%2Fcore@0.1.90.patch +48 -24
- package/src/app.tsx +9 -6
- package/src/cli/registry.test.ts +0 -1
- package/src/components/chart/chart-controller.ts +0 -1
- package/src/components/chart/chart-data.ts +1 -31
- package/src/components/chart/chart-renderer.ts +70 -0
- package/src/components/chart/chart-resolution.test.ts +0 -36
- package/src/components/chart/chart-resolution.ts +0 -27
- package/src/components/chart/chart.test.ts +17 -45
- package/src/components/chart/comparison-chart-data.test.ts +1 -5
- package/src/components/chart/comparison-chart-data.ts +1 -4
- package/src/components/chart/comparison-stock-chart.tsx +62 -77
- package/src/components/chart/native/kitty.test.ts +23 -0
- package/src/components/chart/native/surface-visibility.ts +48 -0
- package/src/components/chart/static-chart-surface.tsx +28 -4
- package/src/components/chart/stock-chart.test.ts +0 -16
- package/src/components/chart/stock-chart.tsx +52 -71
- package/src/components/chart/time-axis-label.tsx +43 -0
- package/src/components/command-bar/command-bar.test.tsx +71 -8
- package/src/components/command-bar/command-bar.tsx +388 -713
- package/src/components/command-bar/confirm-body.tsx +51 -0
- package/src/components/command-bar/list-model.ts +77 -0
- package/src/components/command-bar/view-model.ts +0 -19
- package/src/components/command-bar/workflow-ops.ts +4 -0
- package/src/components/data-table-stack-view.test.tsx +39 -4
- package/src/components/data-table-stack-view.tsx +11 -104
- package/src/components/data-table-view.tsx +43 -79
- package/src/components/feed-data-table-stack-view.tsx +10 -17
- package/src/components/feed-stack-controller.ts +39 -0
- package/src/components/index.ts +6 -2
- package/src/components/layout/floating-pane.tsx +30 -2
- package/src/components/layout/header.tsx +10 -7
- package/src/components/layout/pane-content.tsx +2 -0
- package/src/components/layout/pane-cycle-order.test.ts +30 -0
- package/src/components/layout/pane-cycle-order.ts +32 -0
- package/src/components/layout/pane-footer.tsx +26 -4
- package/src/components/layout/pane-header.tsx +5 -5
- package/src/components/layout/pane-keyboard-scroll.test.tsx +226 -0
- package/src/components/layout/shell.test.tsx +66 -22
- package/src/components/layout/shell.tsx +13 -6
- package/src/components/onboarding/onboarding-wizard.tsx +1 -2
- package/src/components/pane-settings-dialog.tsx +12 -4
- package/src/components/pane-template-wizard.test.tsx +3 -4
- package/src/components/speedometer-gauge.test.tsx +45 -0
- package/src/components/speedometer-gauge.tsx +558 -0
- package/src/components/table-view-shared.tsx +158 -0
- package/src/components/ticker-list-table-view.tsx +42 -72
- package/src/components/ui/confirm-dialog.tsx +66 -0
- package/src/components/ui/data-table.tsx +18 -10
- package/src/components/ui/index.ts +0 -1
- package/src/components/ui/page-stack-view.tsx +39 -10
- package/src/components/ui/remote-image.tsx +15 -40
- package/src/components/ui/table-layout.test.ts +16 -0
- package/src/components/ui/table-layout.ts +10 -3
- package/src/components/ui/ui.test.tsx +100 -20
- package/src/core/state/app-state.ts +6 -3
- package/src/market-data/coordinator.test.ts +1 -1
- package/src/market-data/coordinator.ts +0 -1
- package/src/news/aggregator.ts +0 -3
- package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +46 -16
- package/src/plugins/builtin/ai/providers.ts +0 -17
- package/src/plugins/builtin/ai/screener-pane.test.tsx +3 -4
- package/src/plugins/builtin/ai/screener-pane.tsx +0 -3
- package/src/plugins/builtin/ask-ai.test.tsx +89 -2
- package/src/plugins/builtin/broker-manager/index.test.tsx +2 -2
- package/src/plugins/builtin/broker-manager/index.tsx +19 -36
- package/src/plugins/builtin/chat-controller.test.ts +105 -0
- package/src/plugins/builtin/chat-controller.ts +75 -6
- package/src/plugins/builtin/chat.test.tsx +6 -6
- package/src/plugins/builtin/chat.tsx +29 -20
- package/src/plugins/builtin/cloud-tweets.tsx +0 -1
- package/src/plugins/builtin/comparison-chart.test.tsx +53 -2
- package/src/plugins/builtin/comparison-chart.tsx +11 -5
- package/src/plugins/builtin/correlation/index.tsx +1 -6
- package/src/plugins/builtin/debug.tsx +1 -3
- package/src/plugins/builtin/econ/index.tsx +1 -2
- package/src/plugins/builtin/fear-greed/fear-greed-data.test.ts +72 -0
- package/src/plugins/builtin/fear-greed/fear-greed-data.ts +366 -0
- package/src/plugins/builtin/fear-greed/index.tsx +478 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
- package/src/plugins/builtin/help.test.tsx +2 -2
- package/src/plugins/builtin/help.tsx +2 -2
- package/src/plugins/builtin/holders/index.tsx +10 -15
- package/src/plugins/builtin/insider/index.tsx +22 -89
- package/src/plugins/builtin/market-movers/index.tsx +1 -1
- package/src/plugins/builtin/news-wire/index.tsx +41 -2
- package/src/plugins/builtin/news-wire/news-table.tsx +11 -11
- package/src/plugins/builtin/news.tsx +4 -16
- package/src/plugins/builtin/notes.tsx +18 -55
- package/src/plugins/builtin/options.test.tsx +58 -2
- package/src/plugins/builtin/options.tsx +108 -49
- package/src/plugins/builtin/plugin-groups.ts +2 -0
- package/src/plugins/builtin/portfolio-list/cli/commands.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +5 -21
- package/src/plugins/builtin/sec-filing-display.tsx +84 -0
- package/src/plugins/builtin/sec.tsx +13 -66
- package/src/plugins/builtin/sectors/index.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +0 -1
- package/src/plugins/builtin/ticker-detail/index.tsx +0 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +0 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +36 -28
- package/src/plugins/builtin/ticker-detail-chart-tab-switch.test.tsx +69 -4
- package/src/plugins/builtin/ticker-detail.test.tsx +102 -0
- package/src/plugins/builtin/world-indices/index.tsx +1 -1
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/ibkr/broker-adapter.ts +1 -3
- package/src/plugins/ibkr/gateway-service-native.ts +1 -5
- package/src/plugins/ibkr/gateway-service.ts +1 -1
- package/src/plugins/ibkr/index.test.tsx +0 -2
- package/src/plugins/ibkr/trade-tab.test.tsx +56 -5
- package/src/plugins/ibkr/trade-tab.tsx +13 -11
- package/src/plugins/pane-manager.ts +0 -13
- package/src/plugins/plugin-runtime.test.tsx +6 -6
- package/src/plugins/plugin-runtime.tsx +13 -8
- package/src/plugins/prediction-markets/cli.ts +0 -1
- package/src/plugins/prediction-markets/controller-data.ts +0 -2
- package/src/plugins/prediction-markets/pane.test.tsx +3 -11
- package/src/plugins/prediction-markets/test-helpers.tsx +1 -1
- package/src/plugins/registry.ts +10 -39
- package/src/react/input.ts +11 -2
- package/src/renderers/electrobun/view/data-table.tsx +28 -6
- package/src/renderers/electrobun/view/desktop-controls.tsx +34 -20
- package/src/renderers/electrobun/view/dialog-host.tsx +11 -1
- package/src/renderers/electrobun/view/input-host.tsx +123 -12
- package/src/renderers/electrobun/view/key-event.ts +1 -1
- package/src/renderers/electrobun/view/styles.css +331 -0
- package/src/renderers/electrobun/view/ui-host.tsx +22 -3
- package/src/renderers/opentui/chart-surface.tsx +179 -0
- package/src/renderers/opentui/host.tsx +6 -0
- package/src/renderers/opentui/image-loader.test.ts +25 -0
- package/src/renderers/opentui/image-loader.ts +105 -0
- package/src/renderers/opentui/image-surface.tsx +266 -0
- package/src/renderers/opentui/input-host.tsx +45 -6
- package/src/renderers/opentui/start.tsx +48 -30
- package/src/renderers/opentui/test-utils.tsx +17 -0
- package/src/renderers/opentui/ui-host.tsx +7 -3
- package/src/sources/provider-router.ts +0 -4
- package/src/state/app-context.test.ts +45 -0
- package/src/state/app-context.tsx +4 -0
- package/src/state/pane-scroll-registry.tsx +254 -0
- package/src/state/use-inline-tickers.test.tsx +73 -0
- package/src/state/use-inline-tickers.ts +7 -1
- package/src/test-support/plugin-runtime.ts +3 -2
- package/src/theme/colors.ts +12 -3
- package/src/theme/themes.test.ts +23 -1
- package/src/theme/themes.ts +115 -0
- package/src/types/broker.ts +1 -1
- package/src/types/data-provider.ts +0 -1
- package/src/types/plugin.ts +0 -2
- package/src/ui/host.tsx +4 -0
- package/src/ui/index.tsx +9 -1
- package/src/utils/back-navigation.ts +24 -1
- package/src/version.ts +1 -1
- package/src/plugins/builtin/news-wire/feed-pane.tsx +0 -21
- package/src/plugins/builtin/news-wire/top-pane.tsx +0 -21
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gloomberb",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Bloomberg-style stock portfolio tracker for the terminal",
|
|
5
5
|
"module": "src/index.tsx",
|
|
6
6
|
"type": "module",
|
|
@@ -53,6 +53,7 @@
|
|
|
53
53
|
"@opentui/react": "^0.1.90",
|
|
54
54
|
"@stoqey/ib": "^1.5.3",
|
|
55
55
|
"@tanstack/react-virtual": "^3.13.23",
|
|
56
|
+
"jimp": "1.6.0",
|
|
56
57
|
"opentui-spinner": "^0.0.6",
|
|
57
58
|
"react": "19.2.5",
|
|
58
59
|
"rxjs": "^7.8.2"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
diff --git a/index-e89anq5x.js b/index-e89anq5x.js
|
|
2
|
-
index
|
|
2
|
+
index e3b6cf9f90b13698966eec991fa7a1b2e1e22d96..709940f3998e5cb1caa88642fa68fa9ace80b7b1 100644
|
|
3
3
|
--- a/index-e89anq5x.js
|
|
4
4
|
+++ b/index-e89anq5x.js
|
|
5
5
|
@@ -6328,17 +6328,27 @@ class MouseParser {
|
|
@@ -66,16 +66,39 @@ index e3b6cf9..14a46d7 100644
|
|
|
66
66
|
consumed: index - offset + 1
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
-
@@ -6408,
|
|
69
|
+
@@ -6408,10 +6418,12 @@ class MouseParser {
|
|
70
70
|
consumed: 6
|
|
71
71
|
};
|
|
72
72
|
}
|
|
73
73
|
- decodeSgrEvent(rawButtonCode, wireX, wireY, pressRelease) {
|
|
74
|
+
- const button = rawButtonCode & 3;
|
|
74
75
|
+ decodeSgrEvent(rawButtonCode, wireX, wireY, pressRelease, context = void 0) {
|
|
75
|
-
|
|
76
|
+
+ const encodedButton = rawButtonCode & 3;
|
|
77
|
+
+ const isExtendedButton = (rawButtonCode & 128) !== 0;
|
|
78
|
+
+ const button = isExtendedButton ? 8 + encodedButton : encodedButton;
|
|
76
79
|
const isScroll = (rawButtonCode & 64) !== 0;
|
|
77
|
-
|
|
78
|
-
|
|
80
|
+
- const scrollDirection = !isScroll ? undefined : MouseParser.SCROLL_DIRECTIONS[button];
|
|
81
|
+
+ const scrollDirection = !isScroll ? undefined : MouseParser.SCROLL_DIRECTIONS[encodedButton];
|
|
82
|
+
const isMotion = (rawButtonCode & 32) !== 0;
|
|
83
|
+
const modifiers = {
|
|
84
|
+
shift: (rawButtonCode & 4) !== 0,
|
|
85
|
+
@@ -6422,7 +6434,7 @@ class MouseParser {
|
|
86
|
+
let scrollInfo;
|
|
87
|
+
if (isMotion) {
|
|
88
|
+
const isDragging = this.mouseButtonsPressed.size > 0;
|
|
89
|
+
- if (button === 3) {
|
|
90
|
+
+ if (!isExtendedButton && encodedButton === 3) {
|
|
91
|
+
type = "move";
|
|
92
|
+
} else if (isDragging) {
|
|
93
|
+
type = "drag";
|
|
94
|
+
@@ -6437,17 +6449,32 @@ class MouseParser {
|
|
95
|
+
};
|
|
96
|
+
} else {
|
|
97
|
+
type = pressRelease === "M" ? "down" : "up";
|
|
98
|
+
- if (type === "down" && button !== 3) {
|
|
99
|
+
+ if (type === "down" && (isExtendedButton || encodedButton !== 3)) {
|
|
100
|
+
this.mouseButtonsPressed.add(button);
|
|
101
|
+
} else if (type === "up") {
|
|
79
102
|
this.mouseButtonsPressed.clear();
|
|
80
103
|
}
|
|
81
104
|
}
|
|
@@ -90,9 +113,10 @@ index e3b6cf9..14a46d7 100644
|
|
|
90
113
|
+ const pixelY = pixelModeDetected ? Math.max(wireY - 1, 0) : void 0;
|
|
91
114
|
return {
|
|
92
115
|
type,
|
|
93
|
-
|
|
116
|
+
- button: button === 3 ? 0 : button,
|
|
94
117
|
- x: wireX - 1,
|
|
95
118
|
- y: wireY - 1,
|
|
119
|
+
+ button: !isExtendedButton && encodedButton === 3 ? 0 : button,
|
|
96
120
|
+ x: pixelModeDetected && pixelX !== void 0
|
|
97
121
|
+ ? this.projectPixelToCell(pixelX, context?.pixelWidth ?? 0, context?.terminalWidth ?? 0)
|
|
98
122
|
+ : wireX - 1,
|
|
@@ -104,7 +128,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
104
128
|
modifiers,
|
|
105
129
|
scroll: scrollInfo
|
|
106
130
|
};
|
|
107
|
-
@@ -6856,7 +
|
|
131
|
+
@@ -6856,7 +6883,13 @@ var DEFAULT_PROTOCOL_CONTEXT = {
|
|
108
132
|
kittyKeyboardEnabled: false,
|
|
109
133
|
privateCapabilityRepliesActive: false,
|
|
110
134
|
pixelResolutionQueryActive: false,
|
|
@@ -119,7 +143,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
119
143
|
};
|
|
120
144
|
var RXVT_DOLLAR_CSI_RE = /^\x1b\[\d+\$$/;
|
|
121
145
|
var SYSTEM_CLOCK = new SystemClock;
|
|
122
|
-
@@ -7149,7 +
|
|
146
|
+
@@ -7149,7 +7182,13 @@ class StdinParser {
|
|
123
147
|
kittyKeyboardEnabled: options.protocolContext?.kittyKeyboardEnabled ?? false,
|
|
124
148
|
privateCapabilityRepliesActive: options.protocolContext?.privateCapabilityRepliesActive ?? false,
|
|
125
149
|
pixelResolutionQueryActive: options.protocolContext?.pixelResolutionQueryActive ?? false,
|
|
@@ -134,7 +158,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
134
158
|
};
|
|
135
159
|
}
|
|
136
160
|
get bufferCapacity() {
|
|
137
|
-
@@ -7952,11 +
|
|
161
|
+
@@ -7952,11 +7991,14 @@ class StdinParser {
|
|
138
162
|
});
|
|
139
163
|
}
|
|
140
164
|
emitMouse(rawBytes, encoding) {
|
|
@@ -150,7 +174,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
150
174
|
this.events.push({
|
|
151
175
|
type: "mouse",
|
|
152
176
|
raw: decodeLatin1(rawBytes),
|
|
153
|
-
@@ -17295,6 +
|
|
177
|
+
@@ -17295,6 +17337,8 @@ class MouseEvent {
|
|
154
178
|
button;
|
|
155
179
|
x;
|
|
156
180
|
y;
|
|
@@ -159,7 +183,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
159
183
|
source;
|
|
160
184
|
modifiers;
|
|
161
185
|
scroll;
|
|
162
|
-
@@ -17314,6 +
|
|
186
|
+
@@ -17314,6 +17358,8 @@ class MouseEvent {
|
|
163
187
|
this.button = attributes.button;
|
|
164
188
|
this.x = attributes.x;
|
|
165
189
|
this.y = attributes.y;
|
|
@@ -168,7 +192,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
168
192
|
this.modifiers = attributes.modifiers;
|
|
169
193
|
this.scroll = attributes.scroll;
|
|
170
194
|
this.source = attributes.source;
|
|
171
|
-
@@ -17479,6 +
|
|
195
|
+
@@ -17479,6 +17525,8 @@ class CliRenderer extends EventEmitter8 {
|
|
172
196
|
resizeDebounceDelay = 100;
|
|
173
197
|
enableMouseMovement = false;
|
|
174
198
|
_useMouse = true;
|
|
@@ -177,7 +201,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
177
201
|
autoFocus = true;
|
|
178
202
|
_useAlternateScreen = env.OTUI_USE_ALTERNATE_SCREEN;
|
|
179
203
|
_suspendedMouseEnabled = false;
|
|
180
|
-
@@ -17507,7 +
|
|
204
|
+
@@ -17507,7 +17555,7 @@ class CliRenderer extends EventEmitter8 {
|
|
181
205
|
this.handleResize(width, height);
|
|
182
206
|
}).bind(this);
|
|
183
207
|
_capabilities = null;
|
|
@@ -186,7 +210,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
186
210
|
_hasPointer = false;
|
|
187
211
|
_lastPointerModifiers = { shift: false, alt: false, ctrl: false };
|
|
188
212
|
_currentMousePointerStyle = undefined;
|
|
189
|
-
@@ -17661,7 +
|
|
213
|
+
@@ -17661,7 +17709,13 @@ Captured output:
|
|
190
214
|
kittyKeyboardEnabled: useKittyForParsing,
|
|
191
215
|
privateCapabilityRepliesActive: false,
|
|
192
216
|
pixelResolutionQueryActive: false,
|
|
@@ -201,7 +225,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
201
225
|
},
|
|
202
226
|
clock: this.clock
|
|
203
227
|
});
|
|
204
|
-
@@ -17983,12 +
|
|
228
|
+
@@ -17983,12 +18037,14 @@ Captured output:
|
|
205
229
|
enableMouse() {
|
|
206
230
|
this._useMouse = true;
|
|
207
231
|
this.lib.enableMouse(this.rendererPtr, this.enableMouseMovement);
|
|
@@ -216,7 +240,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
216
240
|
}
|
|
217
241
|
enableKittyKeyboard(flags = 3) {
|
|
218
242
|
this.lib.enableKittyKeyboard(this.rendererPtr, flags);
|
|
219
|
-
@@ -17998,6 +
|
|
243
|
+
@@ -17998,6 +18054,64 @@ Captured output:
|
|
220
244
|
this.lib.disableKittyKeyboard(this.rendererPtr);
|
|
221
245
|
this.updateStdinParserProtocolContext({ kittyKeyboardEnabled: false }, true);
|
|
222
246
|
}
|
|
@@ -281,7 +305,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
281
305
|
set useThread(useThread) {
|
|
282
306
|
this._useThread = useThread;
|
|
283
307
|
this.lib.setUseThread(this.rendererPtr, useThread);
|
|
284
|
-
@@ -18031,6 +
|
|
308
|
+
@@ -18031,6 +18145,7 @@ Captured output:
|
|
285
309
|
if (this._useMouse) {
|
|
286
310
|
this.enableMouse();
|
|
287
311
|
}
|
|
@@ -289,7 +313,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
289
313
|
this.queryPixelResolution();
|
|
290
314
|
}
|
|
291
315
|
stdinListener = ((chunk) => {
|
|
292
|
-
@@ -18070,6 +
|
|
316
|
+
@@ -18070,6 +18185,7 @@ Captured output:
|
|
293
317
|
if (isCapabilityResponse(sequence)) {
|
|
294
318
|
this.lib.processCapabilityResponse(this.rendererPtr, sequence);
|
|
295
319
|
this._capabilities = this.lib.getTerminalCapabilities(this.rendererPtr);
|
|
@@ -297,7 +321,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
297
321
|
this.emit("capabilities" /* CAPABILITIES */, this._capabilities);
|
|
298
322
|
return true;
|
|
299
323
|
}
|
|
300
|
-
@@ -18079,6 +
|
|
324
|
+
@@ -18079,6 +18195,7 @@ Captured output:
|
|
301
325
|
if (sequence === "\x1B[I") {
|
|
302
326
|
if (this.shouldRestoreModesOnNextFocus) {
|
|
303
327
|
this.lib.restoreTerminalModes(this.rendererPtr);
|
|
@@ -305,7 +329,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
305
329
|
this.shouldRestoreModesOnNextFocus = false;
|
|
306
330
|
}
|
|
307
331
|
if (this._terminalFocusState !== true) {
|
|
308
|
-
@@ -18187,6 +
|
|
332
|
+
@@ -18187,6 +18304,7 @@ Captured output:
|
|
309
333
|
}
|
|
310
334
|
this.waitingForPixelResolution = false;
|
|
311
335
|
this.updateStdinParserProtocolContext({ pixelResolutionQueryActive: false }, true);
|
|
@@ -313,7 +337,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
313
337
|
return true;
|
|
314
338
|
}
|
|
315
339
|
return false;
|
|
316
|
-
@@ -18221,9 +
|
|
340
|
+
@@ -18221,9 +18339,15 @@ Captured output:
|
|
317
341
|
return false;
|
|
318
342
|
}
|
|
319
343
|
mouseEvent.y -= this.renderOffset;
|
|
@@ -329,7 +353,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
329
353
|
this._hasPointer = true;
|
|
330
354
|
this._lastPointerModifiers = mouseEvent.modifiers;
|
|
331
355
|
if (this._console.visible) {
|
|
332
|
-
@@ -18352,6 +
|
|
356
|
+
@@ -18352,6 +18476,8 @@ Captured output:
|
|
333
357
|
button: 0,
|
|
334
358
|
x: this._latestPointer.x,
|
|
335
359
|
y: this._latestPointer.y,
|
|
@@ -338,7 +362,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
338
362
|
modifiers: this._lastPointerModifiers
|
|
339
363
|
};
|
|
340
364
|
if (lastOver && !lastOver.isDestroyed) {
|
|
341
|
-
@@ -18435,6 +
|
|
365
|
+
@@ -18435,6 +18561,7 @@ Captured output:
|
|
342
366
|
const prevWidth = this._terminalWidth;
|
|
343
367
|
this._terminalWidth = width;
|
|
344
368
|
this._terminalHeight = height;
|
|
@@ -346,7 +370,7 @@ index e3b6cf9..14a46d7 100644
|
|
|
346
370
|
this.queryPixelResolution();
|
|
347
371
|
this.setCapturedRenderable(undefined);
|
|
348
372
|
this.stdinParser?.resetMouseState();
|
|
349
|
-
@@ -18712,6 +
|
|
373
|
+
@@ -18712,6 +18839,7 @@ Captured output:
|
|
350
374
|
}
|
|
351
375
|
this._isRunning = false;
|
|
352
376
|
this.waitingForPixelResolution = false;
|
package/src/app.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import { Header } from "./components/layout/header";
|
|
|
17
17
|
import { StatusBar } from "./components/layout/status-bar";
|
|
18
18
|
import { Shell } from "./components/layout/shell";
|
|
19
19
|
import { DetachedPaneShell } from "./components/layout/detached-pane-shell";
|
|
20
|
+
import { getVisiblePaneCycleOrder } from "./components/layout/pane-cycle-order";
|
|
20
21
|
import { CommandBar } from "./components/command-bar/command-bar";
|
|
21
22
|
import { OnboardingWizard } from "./components/onboarding/onboarding-wizard";
|
|
22
23
|
import { useDialog, useDialogState } from "./ui/dialog";
|
|
@@ -1441,18 +1442,20 @@ function AppInner({
|
|
|
1441
1442
|
if (state.commandBarOpen || state.inputCaptured) return;
|
|
1442
1443
|
|
|
1443
1444
|
if (event.name === "tab") {
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1445
|
+
const paneOrder = getVisiblePaneCycleOrder(
|
|
1446
|
+
state.config.layout,
|
|
1447
|
+
pluginRegistry,
|
|
1448
|
+
state.config.disabledPlugins,
|
|
1449
|
+
);
|
|
1450
|
+
if (paneOrder.length === 0) return;
|
|
1450
1451
|
|
|
1451
1452
|
if (event.shift) {
|
|
1452
1453
|
dispatch({ type: "FOCUS_PREV", paneOrder });
|
|
1453
1454
|
} else {
|
|
1454
1455
|
dispatch({ type: "FOCUS_NEXT", paneOrder });
|
|
1455
1456
|
}
|
|
1457
|
+
event.preventDefault();
|
|
1458
|
+
event.stopPropagation();
|
|
1456
1459
|
} else if (!isDetachedWindow && event.name === "q" && !(focusedPane?.paneId === "ticker-detail" && focusedDetailTab === "financials")) {
|
|
1457
1460
|
rendererHost.requestExit();
|
|
1458
1461
|
} else if (event.name === "r") {
|
package/src/cli/registry.test.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { join } from "path";
|
|
|
4
4
|
import { tmpdir } from "os";
|
|
5
5
|
import { createDefaultConfig } from "../types/config";
|
|
6
6
|
import type { GloomPlugin } from "../types/plugin";
|
|
7
|
-
import type { LoadedExternalPlugin } from "../plugins/loader";
|
|
8
7
|
import {
|
|
9
8
|
buildCliCommandRegistry,
|
|
10
9
|
createCliCommandContext,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { PricePoint } from "../../types/financials";
|
|
2
|
-
import type { ChartRenderMode
|
|
3
|
-
import { getVisiblePointCount } from "./chart-viewport";
|
|
2
|
+
import type { ChartRenderMode } from "./chart-types";
|
|
4
3
|
|
|
5
4
|
export interface ProjectedChartPoint {
|
|
6
5
|
date: Date;
|
|
@@ -76,34 +75,6 @@ export function resolveRenderMode(
|
|
|
76
75
|
};
|
|
77
76
|
}
|
|
78
77
|
|
|
79
|
-
/**
|
|
80
|
-
* Apply zoom and pan to get the visible data window.
|
|
81
|
-
*/
|
|
82
|
-
export function getVisibleWindow(
|
|
83
|
-
history: PricePoint[],
|
|
84
|
-
viewState: ChartViewState,
|
|
85
|
-
chartWidth: number,
|
|
86
|
-
): VisibleWindow {
|
|
87
|
-
if (history.length === 0) {
|
|
88
|
-
return { points: [], startIdx: 0, endIdx: 0 };
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const visibleCount = getVisiblePointCount(history.length, viewState.zoomLevel);
|
|
92
|
-
|
|
93
|
-
// Clamp pan offset
|
|
94
|
-
const maxPan = Math.max(history.length - visibleCount, 0);
|
|
95
|
-
const pan = Math.min(Math.max(viewState.panOffset, 0), maxPan);
|
|
96
|
-
|
|
97
|
-
const endIdx = history.length - pan;
|
|
98
|
-
const startIdx = Math.max(endIdx - visibleCount, 0);
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
points: history.slice(startIdx, endIdx),
|
|
102
|
-
startIdx,
|
|
103
|
-
endIdx,
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
|
|
107
78
|
function normalizePoint(point: PricePoint): ProjectedChartPoint {
|
|
108
79
|
const open = point.open ?? point.close;
|
|
109
80
|
const high = point.high ?? point.close;
|
|
@@ -334,7 +305,6 @@ export function bucketOhlcSeries(
|
|
|
334
305
|
return result;
|
|
335
306
|
}
|
|
336
307
|
|
|
337
|
-
const MS_HOUR = 3600_000;
|
|
338
308
|
const MS_DAY = 86400_000;
|
|
339
309
|
|
|
340
310
|
/**
|
|
@@ -918,6 +918,76 @@ function writeAxisLabel(axis: string[], start: number, label: string) {
|
|
|
918
918
|
}
|
|
919
919
|
}
|
|
920
920
|
|
|
921
|
+
export interface AxisLabelSegment {
|
|
922
|
+
text: string;
|
|
923
|
+
highlighted: boolean;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
function formatCursorTimeAxisValue(
|
|
927
|
+
value: Date | string | number,
|
|
928
|
+
dates: Array<Date | string | number>,
|
|
929
|
+
width: number,
|
|
930
|
+
): string {
|
|
931
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
932
|
+
if (isNaN(date.getTime())) return "—";
|
|
933
|
+
|
|
934
|
+
const normalizedDates = dates.map((entry) => (entry instanceof Date ? entry : new Date(entry)));
|
|
935
|
+
const validDates = normalizedDates.filter((entry) => !isNaN(entry.getTime()));
|
|
936
|
+
if (validDates.length === 0) return formatDateShort(date);
|
|
937
|
+
|
|
938
|
+
const first = validDates[0]!;
|
|
939
|
+
const last = validDates[validDates.length - 1]!;
|
|
940
|
+
const spanMs = Math.max(last.getTime() - first.getTime(), 1);
|
|
941
|
+
const roughLabelCount = Math.max(Math.floor(normalizeCount(width, 0) / 10), 2);
|
|
942
|
+
const minGapMs = getMinPositiveGapMs(validDates);
|
|
943
|
+
const effectiveStepMs = Math.max(spanMs / Math.max(roughLabelCount - 1, 1), minGapMs || 0);
|
|
944
|
+
const unit = resolveAxisLabelUnit(effectiveStepMs);
|
|
945
|
+
const counterpart = isSameCalendarDay(first, last)
|
|
946
|
+
? first
|
|
947
|
+
: isSameCalendarDay(date, first)
|
|
948
|
+
? last
|
|
949
|
+
: first;
|
|
950
|
+
|
|
951
|
+
return formatTimeAxisBoundaryLabel(date, unit, counterpart);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
export function buildCursorTimeAxisSegments({
|
|
955
|
+
timeLabels,
|
|
956
|
+
width,
|
|
957
|
+
cursorColumn,
|
|
958
|
+
cursorDate,
|
|
959
|
+
dates,
|
|
960
|
+
}: {
|
|
961
|
+
timeLabels: string;
|
|
962
|
+
width: number;
|
|
963
|
+
cursorColumn: number | null;
|
|
964
|
+
cursorDate: Date | string | number | null;
|
|
965
|
+
dates: Array<Date | string | number>;
|
|
966
|
+
}): AxisLabelSegment[] {
|
|
967
|
+
const axisWidth = normalizeCount(width, 0);
|
|
968
|
+
if (axisWidth <= 0) return [];
|
|
969
|
+
|
|
970
|
+
const base = timeLabels.padEnd(axisWidth).slice(0, axisWidth);
|
|
971
|
+
if (cursorColumn === null || cursorDate === null) {
|
|
972
|
+
return [{ text: base, highlighted: false }];
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
const label = formatCursorTimeAxisValue(cursorDate, dates, axisWidth);
|
|
976
|
+
if (!label) return [{ text: base, highlighted: false }];
|
|
977
|
+
|
|
978
|
+
const axis = base.split("");
|
|
979
|
+
const column = Math.max(Math.min(Math.round(cursorColumn), axisWidth - 1), 0);
|
|
980
|
+
const start = resolveAxisLabelStart(column, label, axisWidth);
|
|
981
|
+
const end = Math.min(start + label.length, axisWidth);
|
|
982
|
+
writeAxisLabel(axis, start, label);
|
|
983
|
+
|
|
984
|
+
return [
|
|
985
|
+
{ text: axis.slice(0, start).join(""), highlighted: false },
|
|
986
|
+
{ text: axis.slice(start, end).join(""), highlighted: true },
|
|
987
|
+
{ text: axis.slice(end).join(""), highlighted: false },
|
|
988
|
+
].filter((segment) => segment.text.length > 0);
|
|
989
|
+
}
|
|
990
|
+
|
|
921
991
|
export function buildTimeAxis(dates: Array<Date | string | number>, width: number): string {
|
|
922
992
|
const axisWidth = normalizeCount(width, 0);
|
|
923
993
|
if (dates.length === 0 || axisWidth <= 0) return "";
|
|
@@ -3,10 +3,8 @@ import {
|
|
|
3
3
|
CHART_RESOLUTION_STEP_MS,
|
|
4
4
|
clampTimeRangeForResolution,
|
|
5
5
|
getActiveRangePreset,
|
|
6
|
-
getBestSupportedResolutionForDateWindow,
|
|
7
6
|
getBestSupportedResolutionForVisibleWindow,
|
|
8
7
|
getPresetResolution,
|
|
9
|
-
intersectChartResolutions,
|
|
10
8
|
isRangePresetSupported,
|
|
11
9
|
normalizeChartResolution,
|
|
12
10
|
sortChartResolutions,
|
|
@@ -30,14 +28,6 @@ describe("chart-resolution", () => {
|
|
|
30
28
|
expect(getActiveRangePreset("1Y", "1d", 1, 2)).toBeNull();
|
|
31
29
|
});
|
|
32
30
|
|
|
33
|
-
test("intersects and sorts provider capabilities", () => {
|
|
34
|
-
expect(intersectChartResolutions([
|
|
35
|
-
["1d", "5m", "1wk", "nope"],
|
|
36
|
-
["1wk", "1d", "1mo"],
|
|
37
|
-
["1d", "1wk"],
|
|
38
|
-
])).toEqual(["1d", "1wk"]);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
31
|
test("checks whether a range preset is supported by the visible capability set", () => {
|
|
42
32
|
expect(isRangePresetSupported("1Y", ["1d", "1wk"])).toBe(true);
|
|
43
33
|
expect(isRangePresetSupported("ALL", ["1d", "1wk"])).toBe(false);
|
|
@@ -57,32 +47,6 @@ describe("chart-resolution", () => {
|
|
|
57
47
|
expect(sortChartResolutions(["1wk", "auto", "15m", "1d"])).toEqual(["auto", "15m", "1d", "1wk"]);
|
|
58
48
|
});
|
|
59
49
|
|
|
60
|
-
test("picks the best supported resolution for the current visible date window", () => {
|
|
61
|
-
const support = [
|
|
62
|
-
{ resolution: "1m", maxRange: "1D" },
|
|
63
|
-
{ resolution: "5m", maxRange: "1W" },
|
|
64
|
-
{ resolution: "15m", maxRange: "1M" },
|
|
65
|
-
{ resolution: "1h", maxRange: "3M" },
|
|
66
|
-
{ resolution: "1d", maxRange: "5Y" },
|
|
67
|
-
{ resolution: "1wk", maxRange: "ALL" },
|
|
68
|
-
] as const;
|
|
69
|
-
|
|
70
|
-
expect(getBestSupportedResolutionForDateWindow({
|
|
71
|
-
start: new Date("2026-01-08T10:00:00Z"),
|
|
72
|
-
end: new Date("2026-01-08T16:00:00Z"),
|
|
73
|
-
}, support)).toBe("1m");
|
|
74
|
-
|
|
75
|
-
expect(getBestSupportedResolutionForDateWindow({
|
|
76
|
-
start: new Date("2026-01-01T00:00:00Z"),
|
|
77
|
-
end: new Date("2026-01-08T00:00:00Z"),
|
|
78
|
-
}, support)).toBe("5m");
|
|
79
|
-
|
|
80
|
-
expect(getBestSupportedResolutionForDateWindow({
|
|
81
|
-
start: new Date("2021-01-01T00:00:00Z"),
|
|
82
|
-
end: new Date("2026-01-01T00:00:00Z"),
|
|
83
|
-
}, support)).toBe("1wk");
|
|
84
|
-
});
|
|
85
|
-
|
|
86
50
|
test("picks a denser supported resolution as the visible window narrows", () => {
|
|
87
51
|
const support = [
|
|
88
52
|
{ resolution: "1m", maxRange: "1D" },
|
|
@@ -151,24 +151,6 @@ export function sortChartResolutions<T extends ChartResolution>(resolutions: rea
|
|
|
151
151
|
));
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
function normalizeManualChartResolutions(resolutions: readonly string[]): ManualChartResolution[] {
|
|
155
|
-
const seen = new Set<ManualChartResolution>();
|
|
156
|
-
for (const resolution of resolutions) {
|
|
157
|
-
if (isManualChartResolution(resolution)) {
|
|
158
|
-
seen.add(resolution);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
return sortChartResolutions([...seen]);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
export function intersectChartResolutions(resolutionSets: Array<readonly string[]>): ManualChartResolution[] {
|
|
165
|
-
if (resolutionSets.length === 0) return [];
|
|
166
|
-
const [first = new Set<ManualChartResolution>(), ...rest] = resolutionSets.map((set) => new Set(normalizeManualChartResolutions(set)));
|
|
167
|
-
return sortChartResolutions(
|
|
168
|
-
[...first].filter((resolution) => rest.every((set) => set.has(resolution))),
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
154
|
function sortChartResolutionSupport(support: readonly ChartResolutionSupport[]): ChartResolutionSupport[] {
|
|
173
155
|
return [...support].sort((left, right) => (
|
|
174
156
|
CHART_RESOLUTION_ORDER.indexOf(left.resolution) - CHART_RESOLUTION_ORDER.indexOf(right.resolution)
|
|
@@ -324,15 +306,6 @@ export function getBestSupportedResolutionForPreset(
|
|
|
324
306
|
return supportedResolutions[supportedResolutions.length - 1] ?? null;
|
|
325
307
|
}
|
|
326
308
|
|
|
327
|
-
export function getBestSupportedResolutionForDateWindow(
|
|
328
|
-
window: { start: Date | null; end: Date | null } | null,
|
|
329
|
-
support: readonly ChartResolutionSupport[] | ReadonlyMap<ManualChartResolution, TimeRange>,
|
|
330
|
-
): ManualChartResolution | null {
|
|
331
|
-
if (!window?.start || !window.end) return null;
|
|
332
|
-
const range = getTimeRangeForDateWindow(window);
|
|
333
|
-
return getBestSupportedResolutionForPreset(range, support);
|
|
334
|
-
}
|
|
335
|
-
|
|
336
309
|
export function getTimeRangeForDateWindow(
|
|
337
310
|
window: { start: Date | null; end: Date | null } | null,
|
|
338
311
|
): TimeRange {
|
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
3
|
bucketOhlcSeries,
|
|
4
|
-
getVisibleWindow,
|
|
5
4
|
projectChartData,
|
|
6
5
|
resolveRenderMode,
|
|
7
6
|
resolveStableOhlcProjectionOptions,
|
|
8
7
|
} from "./chart-data";
|
|
9
8
|
import { stepCursorTowards } from "./cursor-motion";
|
|
10
|
-
import { buildChartScene, buildTimeAxis, formatAxisValue, formatCursorAxisValue, renderChart, resolveChartAxisWidth, resolveChartPalette } from "./chart-renderer";
|
|
9
|
+
import { buildChartScene, buildCursorTimeAxisSegments, buildTimeAxis, formatAxisValue, formatCursorAxisValue, renderChart, resolveChartAxisWidth, resolveChartPalette } from "./chart-renderer";
|
|
11
10
|
import type { PricePoint } from "../../types/financials";
|
|
12
|
-
import type { ChartRenderMode
|
|
11
|
+
import type { ChartRenderMode } from "./chart-types";
|
|
13
12
|
|
|
14
13
|
const aggregationFixture: PricePoint[] = [
|
|
15
14
|
{ date: new Date("2024-01-02"), close: 10, volume: 100 },
|
|
@@ -225,48 +224,6 @@ describe("resolveRenderMode", () => {
|
|
|
225
224
|
});
|
|
226
225
|
});
|
|
227
226
|
|
|
228
|
-
describe("getVisibleWindow", () => {
|
|
229
|
-
test("shows the full selected range at default zoom and lets the renderer downsample it", () => {
|
|
230
|
-
const history = buildDenseHistory(252);
|
|
231
|
-
const viewState: ChartViewState = {
|
|
232
|
-
presetRange: "1Y",
|
|
233
|
-
bufferRange: "1Y",
|
|
234
|
-
activePreset: "1Y",
|
|
235
|
-
resolution: "auto",
|
|
236
|
-
panOffset: 0,
|
|
237
|
-
zoomLevel: 1,
|
|
238
|
-
cursorX: null,
|
|
239
|
-
cursorY: null,
|
|
240
|
-
};
|
|
241
|
-
|
|
242
|
-
const window = getVisibleWindow(history, viewState, 80);
|
|
243
|
-
|
|
244
|
-
expect(window.points).toHaveLength(history.length);
|
|
245
|
-
expect(window.points[0]?.date.toISOString()).toBe(history[0]!.date.toISOString());
|
|
246
|
-
expect(window.points.at(-1)?.date.toISOString()).toBe(history.at(-1)!.date.toISOString());
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test("zooms into the selected range instead of pinning the default view to chart width", () => {
|
|
250
|
-
const history = buildDenseHistory(252);
|
|
251
|
-
const viewState: ChartViewState = {
|
|
252
|
-
presetRange: "1Y",
|
|
253
|
-
bufferRange: "1Y",
|
|
254
|
-
activePreset: "1Y",
|
|
255
|
-
resolution: "auto",
|
|
256
|
-
panOffset: 0,
|
|
257
|
-
zoomLevel: 2,
|
|
258
|
-
cursorX: null,
|
|
259
|
-
cursorY: null,
|
|
260
|
-
};
|
|
261
|
-
|
|
262
|
-
const window = getVisibleWindow(history, viewState, 80);
|
|
263
|
-
|
|
264
|
-
expect(window.points).toHaveLength(126);
|
|
265
|
-
expect(window.points[0]?.date.toISOString()).toBe(history[126]!.date.toISOString());
|
|
266
|
-
expect(window.points.at(-1)?.date.toISOString()).toBe(history.at(-1)!.date.toISOString());
|
|
267
|
-
});
|
|
268
|
-
});
|
|
269
|
-
|
|
270
227
|
describe("renderChart", () => {
|
|
271
228
|
test("sizes the y-axis to the visible label width instead of a fixed gutter", () => {
|
|
272
229
|
expect(resolveChartAxisWidth(["$18", "$17", "$16", "$14"], 4, 10)).toBe(4);
|
|
@@ -457,6 +414,21 @@ describe("renderChart", () => {
|
|
|
457
414
|
);
|
|
458
415
|
});
|
|
459
416
|
|
|
417
|
+
test("overlays the cursor date on the x-axis without changing its width", () => {
|
|
418
|
+
const dates = Array.from({ length: 13 }, (_, index) => new Date(2026, 0, 5, 9, 30 + index * 30));
|
|
419
|
+
const width = 72;
|
|
420
|
+
const segments = buildCursorTimeAxisSegments({
|
|
421
|
+
timeLabels: buildTimeAxis(dates, width),
|
|
422
|
+
width,
|
|
423
|
+
cursorColumn: 36,
|
|
424
|
+
cursorDate: dates[6]!,
|
|
425
|
+
dates,
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
expect(segments.map((segment) => segment.text).join("")).toHaveLength(width);
|
|
429
|
+
expect(segments.find((segment) => segment.highlighted)?.text).toBe("12:30");
|
|
430
|
+
});
|
|
431
|
+
|
|
460
432
|
test("shows second precision when the visible window reaches second-level data", () => {
|
|
461
433
|
const dates = Array.from({ length: 6 }, (_, index) => new Date(2026, 0, 5, 9, 30, index * 10));
|
|
462
434
|
|
|
@@ -25,10 +25,9 @@ describe("projectComparisonChartData", () => {
|
|
|
25
25
|
const window = getVisibleComparisonWindow([
|
|
26
26
|
makeSeries("AAPL", "USD", closes),
|
|
27
27
|
], {
|
|
28
|
-
timeRange: "ALL",
|
|
29
28
|
panOffset: 0,
|
|
30
29
|
zoomLevel: 1,
|
|
31
|
-
}
|
|
30
|
+
});
|
|
32
31
|
|
|
33
32
|
expect(window.dates).toHaveLength(120);
|
|
34
33
|
expect(window.dates[0]?.toISOString()).toBe("2024-01-01T00:00:00.000Z");
|
|
@@ -48,7 +47,6 @@ describe("projectComparisonChartData", () => {
|
|
|
48
47
|
["2024-01-05", 22],
|
|
49
48
|
]),
|
|
50
49
|
], 8, {
|
|
51
|
-
timeRange: "ALL",
|
|
52
50
|
panOffset: 0,
|
|
53
51
|
zoomLevel: 1,
|
|
54
52
|
renderMode: "line",
|
|
@@ -72,7 +70,6 @@ describe("projectComparisonChartData", () => {
|
|
|
72
70
|
["2024-01-04", 60],
|
|
73
71
|
]),
|
|
74
72
|
], 8, {
|
|
75
|
-
timeRange: "ALL",
|
|
76
73
|
panOffset: 0,
|
|
77
74
|
zoomLevel: 1,
|
|
78
75
|
renderMode: "line",
|
|
@@ -94,7 +91,6 @@ describe("projectComparisonChartData", () => {
|
|
|
94
91
|
["2024-01-03", 2100],
|
|
95
92
|
]),
|
|
96
93
|
], 8, {
|
|
97
|
-
timeRange: "ALL",
|
|
98
94
|
panOffset: 0,
|
|
99
95
|
zoomLevel: 1,
|
|
100
96
|
renderMode: "line",
|
|
@@ -83,9 +83,7 @@ function getUniqueSortedDates(series: ComparisonChartSeries[]): Date[] {
|
|
|
83
83
|
|
|
84
84
|
export function getMaxComparisonPanOffset(
|
|
85
85
|
series: ComparisonChartSeries[],
|
|
86
|
-
_timeRange: TimeRange,
|
|
87
86
|
zoomLevel: number,
|
|
88
|
-
chartWidth: number,
|
|
89
87
|
): number {
|
|
90
88
|
const dates = getUniqueSortedDates(series);
|
|
91
89
|
const visibleCount = getVisiblePointCount(dates.length, zoomLevel);
|
|
@@ -95,7 +93,6 @@ export function getMaxComparisonPanOffset(
|
|
|
95
93
|
export function getVisibleComparisonWindow(
|
|
96
94
|
series: ComparisonChartSeries[],
|
|
97
95
|
viewState: Pick<ComparisonChartViewState, "panOffset" | "zoomLevel">,
|
|
98
|
-
chartWidth: number,
|
|
99
96
|
): ComparisonVisibleWindow {
|
|
100
97
|
const dates = getUniqueSortedDates(series);
|
|
101
98
|
|
|
@@ -214,7 +211,7 @@ export function projectComparisonChartData(
|
|
|
214
211
|
requestedAxisMode: ChartAxisMode,
|
|
215
212
|
): ComparisonChartProjection {
|
|
216
213
|
const normalizedSeries = series.map((entry) => ({ ...entry, points: normalizeSeriesPoints(entry.points) }));
|
|
217
|
-
const window = getVisibleComparisonWindow(normalizedSeries, viewState
|
|
214
|
+
const window = getVisibleComparisonWindow(normalizedSeries, viewState);
|
|
218
215
|
const axisResolution = resolveComparisonAxisMode(requestedAxisMode, normalizedSeries, window.dates);
|
|
219
216
|
const requestedMode = normalizeComparisonMode(viewState.renderMode);
|
|
220
217
|
if (window.dates.length === 0) {
|