gloomberb 0.3.7 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -8
- package/package.json +1 -1
- package/src/app.tsx +29 -65
- package/src/cli/base-converter.ts +31 -0
- package/src/cli/commands/search.ts +11 -3
- package/src/cli/commands/ticker.ts +22 -11
- package/src/cli/context.ts +27 -32
- package/src/cli/helpers.ts +1 -4
- package/src/cli/index.ts +140 -123
- package/src/cli/registry.test.ts +226 -0
- package/src/cli/registry.ts +201 -0
- package/src/cli.test.ts +219 -0
- package/src/components/chart/chart-controller.ts +309 -0
- package/src/components/chart/chart-data.ts +5 -6
- package/src/components/chart/chart-pane-settings.ts +27 -0
- package/src/components/chart/chart-resolution.test.ts +56 -0
- package/src/components/chart/chart-resolution.ts +362 -0
- package/src/components/chart/chart-types.ts +12 -4
- package/src/components/chart/chart-viewport.test.ts +25 -0
- package/src/components/chart/chart-viewport.ts +28 -0
- package/src/components/chart/comparison-chart-data.ts +20 -27
- package/src/components/chart/comparison-stock-chart.tsx +374 -99
- package/src/components/chart/stock-chart-renderer-switch.test.tsx +124 -0
- package/src/components/chart/stock-chart.tsx +455 -106
- package/src/components/command-bar/__snapshots__/command-bar.test.tsx.snap +4 -4
- package/src/components/command-bar/command-bar.test.tsx +650 -5
- package/src/components/command-bar/command-bar.tsx +359 -63
- package/src/components/command-bar/command-registry.ts +8 -0
- package/src/components/command-bar/helpers.test.ts +23 -1
- package/src/components/command-bar/helpers.ts +14 -5
- package/src/components/command-bar/pane-template-display.ts +12 -0
- package/src/components/command-bar/root-shortcuts.ts +2 -1
- package/src/components/command-bar/workflow-ops.ts +4 -2
- package/src/components/detail-feed-view.test.tsx +124 -49
- package/src/components/detail-feed-view.tsx +396 -160
- package/src/components/layout/floating-pane.tsx +1 -1
- package/src/components/layout/pane-header.tsx +26 -2
- package/src/components/layout/shell.tsx +62 -50
- package/src/components/layout/status-bar.test.tsx +66 -0
- package/src/components/layout/status-bar.tsx +26 -5
- package/src/components/onboarding/onboarding-wizard.test.tsx +65 -0
- package/src/components/onboarding/onboarding-wizard.tsx +5 -6
- package/src/components/ticker-list-table.test.tsx +83 -0
- package/src/components/ticker-list-table.tsx +174 -97
- package/src/components/ui/data-table.tsx +27 -2
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/page-stack-view.tsx +60 -0
- package/src/components/use-double-click-activation.ts +31 -0
- package/src/index.tsx +10 -10
- package/src/market-data/coordinator.test.ts +92 -4
- package/src/market-data/coordinator.ts +32 -5
- package/src/market-data/hooks.test.tsx +120 -0
- package/src/market-data/hooks.tsx +53 -16
- package/src/market-data/request-types.ts +4 -2
- package/src/market-data/selectors.ts +24 -58
- package/src/plugins/builtin/chat-controller.test.ts +370 -3
- package/src/plugins/builtin/chat-controller.ts +310 -35
- package/src/plugins/builtin/chat.test.tsx +118 -6
- package/src/plugins/builtin/chat.tsx +175 -116
- package/src/plugins/builtin/comparison-chart.test.tsx +66 -5
- package/src/plugins/builtin/comparison-chart.tsx +21 -1
- package/src/plugins/builtin/news.tsx +1 -11
- package/src/plugins/builtin/portfolio-list/cli/commands.ts +656 -0
- package/src/plugins/builtin/portfolio-list/command-bar.ts +122 -0
- package/src/plugins/builtin/portfolio-list/header.tsx +16 -34
- package/src/plugins/builtin/portfolio-list/index.tsx +5 -0
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +61 -0
- package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
- package/src/plugins/builtin/portfolio-list/mutations.test.ts +187 -0
- package/src/plugins/builtin/portfolio-list/mutations.ts +226 -0
- package/src/plugins/builtin/portfolio-list/pane.tsx +39 -17
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -2
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.test.tsx +163 -0
- package/src/plugins/builtin/portfolio-list/use-throttled-cursor-symbol.ts +81 -0
- package/src/plugins/builtin/sec.tsx +1 -11
- package/src/plugins/builtin/ticker-detail/chart-tab.tsx +13 -2
- package/src/plugins/builtin/ticker-detail/financials-tab.tsx +21 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +45 -10
- package/src/plugins/builtin/ticker-detail/pane.tsx +35 -17
- package/src/plugins/builtin/ticker-detail/settings.ts +19 -1
- package/src/plugins/builtin/ticker-detail.test.tsx +47 -0
- package/src/plugins/catalog.ts +65 -0
- package/src/plugins/ibkr/broker-adapter.ts +28 -0
- package/src/plugins/ibkr/gateway-service.test.ts +45 -1
- package/src/plugins/ibkr/gateway-service.ts +141 -7
- package/src/plugins/pane-manager.test.ts +1 -1
- package/src/plugins/plugin-runtime.tsx +27 -7
- package/src/plugins/prediction-markets/cli.ts +28 -0
- package/src/plugins/prediction-markets/controller-data.ts +11 -3
- package/src/plugins/prediction-markets/controller.ts +141 -121
- package/src/plugins/prediction-markets/index.tsx +2 -0
- package/src/plugins/prediction-markets/launch.ts +51 -16
- package/src/plugins/prediction-markets/metrics.ts +9 -5
- package/src/plugins/prediction-markets/pane.test.tsx +119 -57
- package/src/plugins/prediction-markets/pane.tsx +74 -153
- package/src/plugins/prediction-markets/table.tsx +6 -3
- package/src/plugins/prediction-markets/types.ts +0 -1
- package/src/sources/gloomberb-cloud.test.ts +29 -0
- package/src/sources/gloomberb-cloud.ts +89 -1
- package/src/sources/provider-router.test.ts +265 -3
- package/src/sources/provider-router.ts +239 -11
- package/src/sources/yahoo-finance.test.ts +26 -0
- package/src/sources/yahoo-finance.ts +54 -3
- package/src/state/app-context-selector.test.tsx +99 -0
- package/src/state/app-context.test.ts +18 -0
- package/src/state/app-context.tsx +170 -65
- package/src/state/selectors.ts +25 -0
- package/src/theme/colors.ts +8 -8
- package/src/theme/themes.test.ts +2 -0
- package/src/theme/themes.ts +22 -10
- package/src/types/broker.ts +21 -0
- package/src/types/config.ts +17 -1
- package/src/types/data-provider.ts +18 -0
- package/src/types/financials.ts +31 -1
- package/src/types/plugin.ts +70 -0
- package/src/utils/api-client.test.ts +57 -0
- package/src/utils/api-client.ts +35 -12
- package/src/utils/back-navigation.ts +27 -0
- package/src/utils/format.test.ts +14 -0
- package/src/utils/format.ts +3 -3
- package/src/utils/market-status.ts +46 -1
- package/src/utils/quote-freshness.ts +127 -0
- package/src/utils/quote-resolution.test.ts +277 -0
- package/src/utils/quote-resolution.ts +641 -0
- package/src/utils/slugify.ts +3 -0
- package/src/utils/timestamp.ts +25 -0
- package/src/version.ts +1 -1
- package/src/cli/commands/portfolio.ts +0 -210
- package/src/cli/commands/watchlist.ts +0 -210
- package/src/plugins/builtin/data-providers.ts +0 -16
- package/src/sources/exchange-rates.ts +0 -18
- package/src/utils/ascii-chart.ts +0 -172
- package/src/utils/ticker-tokenizer.test.ts +0 -48
|
@@ -21,7 +21,6 @@ import type {
|
|
|
21
21
|
PredictionBrowseTab,
|
|
22
22
|
PredictionCategoryId,
|
|
23
23
|
PredictionDetailTab,
|
|
24
|
-
PredictionFocusRegion,
|
|
25
24
|
PredictionHistoryRange,
|
|
26
25
|
PredictionListRow,
|
|
27
26
|
PredictionOrderPreviewIntent,
|
|
@@ -29,7 +28,6 @@ import type {
|
|
|
29
28
|
PredictionVenueScope,
|
|
30
29
|
} from "./types";
|
|
31
30
|
|
|
32
|
-
const EMPTY_SELECTIONS: Record<string, string | null> = {};
|
|
33
31
|
const KEYBOARD_DETAIL_LOAD_DELAY_MS = 140;
|
|
34
32
|
|
|
35
33
|
export function usePredictionMarketsController({
|
|
@@ -63,10 +61,6 @@ export function usePredictionMarketsController({
|
|
|
63
61
|
"detailTab",
|
|
64
62
|
"overview",
|
|
65
63
|
);
|
|
66
|
-
const [focusRegion, setFocusRegion] = usePluginPaneState<PredictionFocusRegion>(
|
|
67
|
-
"focusRegion",
|
|
68
|
-
"list",
|
|
69
|
-
);
|
|
70
64
|
const [searchQuery, setSearchQuery] = usePluginPaneState<string>(
|
|
71
65
|
"searchQuery",
|
|
72
66
|
"",
|
|
@@ -77,22 +71,19 @@ export function usePredictionMarketsController({
|
|
|
77
71
|
);
|
|
78
72
|
const [historyRange, setHistoryRange] =
|
|
79
73
|
usePluginPaneState<PredictionHistoryRange>("historyRange", "1M");
|
|
80
|
-
const [detailSplitRatio, setDetailSplitRatio] = usePluginPaneState<number>(
|
|
81
|
-
"detailSplitRatio",
|
|
82
|
-
0.42,
|
|
83
|
-
);
|
|
84
74
|
const [selectedRowKey, setSelectedRowKey] = usePluginPaneState<
|
|
85
75
|
string | null
|
|
86
76
|
>("selectedRowKey", null);
|
|
87
77
|
const [selectedDetailMarketKey, setSelectedDetailMarketKey] =
|
|
88
78
|
usePluginPaneState<string | null>("selectedDetailMarketKey", null);
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
79
|
+
const defaultSortPreference = useMemo(
|
|
80
|
+
() => getDefaultPredictionSort(paneSettings.defaultBrowseTab),
|
|
81
|
+
[paneSettings.defaultBrowseTab],
|
|
82
|
+
);
|
|
92
83
|
const [sortPreference, setSortPreference] =
|
|
93
84
|
usePluginPaneState<PredictionSortPreference>(
|
|
94
85
|
"sortPreference",
|
|
95
|
-
|
|
86
|
+
defaultSortPreference,
|
|
96
87
|
);
|
|
97
88
|
const [, setOrderPreviewIntent] =
|
|
98
89
|
usePluginPaneState<PredictionOrderPreviewIntent | null>(
|
|
@@ -101,6 +92,7 @@ export function usePredictionMarketsController({
|
|
|
101
92
|
);
|
|
102
93
|
|
|
103
94
|
const [hoveredIdx, setHoveredIdx] = useState<number | null>(null);
|
|
95
|
+
const [detailOpen, setDetailOpen] = useState(false);
|
|
104
96
|
const [searchFocused, setSearchFocused] = useState(false);
|
|
105
97
|
const [initialParamsApplied, setInitialParamsApplied] = useState(false);
|
|
106
98
|
|
|
@@ -126,6 +118,7 @@ export function usePredictionMarketsController({
|
|
|
126
118
|
const data = usePredictionMarketsDataState({
|
|
127
119
|
browseTab,
|
|
128
120
|
categoryId,
|
|
121
|
+
detailOpen,
|
|
129
122
|
effectiveVenueScope,
|
|
130
123
|
focused,
|
|
131
124
|
historyRange,
|
|
@@ -197,14 +190,13 @@ export function usePredictionMarketsController({
|
|
|
197
190
|
]);
|
|
198
191
|
|
|
199
192
|
useEffect(() => {
|
|
200
|
-
const nextDefault = getDefaultPredictionSort(browseTab);
|
|
201
193
|
setSortPreference((current) =>
|
|
202
|
-
current.columnId ===
|
|
203
|
-
current.direction ===
|
|
194
|
+
current.columnId === defaultSortPreference.columnId &&
|
|
195
|
+
current.direction === defaultSortPreference.direction
|
|
204
196
|
? current
|
|
205
|
-
:
|
|
197
|
+
: defaultSortPreference,
|
|
206
198
|
);
|
|
207
|
-
}, [
|
|
199
|
+
}, [defaultSortPreference, setSortPreference]);
|
|
208
200
|
|
|
209
201
|
useEffect(() => {
|
|
210
202
|
const nextFilterResetKey = [
|
|
@@ -231,7 +223,7 @@ export function usePredictionMarketsController({
|
|
|
231
223
|
if (headerScrollBox) {
|
|
232
224
|
headerScrollBox.scrollLeft = 0;
|
|
233
225
|
}
|
|
234
|
-
|
|
226
|
+
setDetailOpen(false);
|
|
235
227
|
setSelectedRowKey((current) => (current == null ? current : null));
|
|
236
228
|
setSelectedDetailMarketKey((current) => (current == null ? current : null));
|
|
237
229
|
}, [
|
|
@@ -239,54 +231,73 @@ export function usePredictionMarketsController({
|
|
|
239
231
|
categoryId,
|
|
240
232
|
data.debouncedSearchQuery,
|
|
241
233
|
effectiveVenueScope,
|
|
242
|
-
setFocusRegion,
|
|
243
234
|
setSelectedDetailMarketKey,
|
|
244
235
|
setSelectedRowKey,
|
|
245
236
|
]);
|
|
246
237
|
|
|
247
238
|
useEffect(() => {
|
|
248
239
|
if (data.visibleRows.length === 0) {
|
|
249
|
-
if (selectedRowKey !== null)
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
240
|
+
if (selectedRowKey !== null) {
|
|
241
|
+
setSelectedRowKey(null);
|
|
242
|
+
}
|
|
243
|
+
if (selectedDetailMarketKey !== null) {
|
|
244
|
+
setSelectedDetailMarketKey(null);
|
|
245
|
+
}
|
|
246
|
+
if (detailOpen) {
|
|
247
|
+
setDetailOpen(false);
|
|
248
|
+
}
|
|
253
249
|
}
|
|
250
|
+
}, [
|
|
251
|
+
data.visibleRows.length,
|
|
252
|
+
detailOpen,
|
|
253
|
+
selectedDetailMarketKey,
|
|
254
|
+
selectedRowKey,
|
|
255
|
+
setSelectedDetailMarketKey,
|
|
256
|
+
setSelectedRowKey,
|
|
257
|
+
]);
|
|
254
258
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
if (current[effectiveVenueScope] === data.selectedRow!.key) {
|
|
258
|
-
return current;
|
|
259
|
-
}
|
|
260
|
-
return { ...current, [effectiveVenueScope]: data.selectedRow!.key };
|
|
261
|
-
});
|
|
262
|
-
if (
|
|
263
|
-
!selectedDetailMarketKey ||
|
|
264
|
-
!data.selectedRow.markets.some(
|
|
265
|
-
(market) => market.key === selectedDetailMarketKey,
|
|
266
|
-
)
|
|
267
|
-
) {
|
|
268
|
-
setSelectedDetailMarketKey(data.selectedRow.focusMarketKey);
|
|
269
|
-
}
|
|
259
|
+
useEffect(() => {
|
|
260
|
+
if (selectedRowKey == null || data.selectedRow) {
|
|
270
261
|
return;
|
|
271
262
|
}
|
|
272
263
|
|
|
273
|
-
if (
|
|
274
|
-
|
|
264
|
+
if (detailOpen) {
|
|
265
|
+
setDetailOpen(false);
|
|
275
266
|
}
|
|
276
267
|
if (selectedDetailMarketKey !== null) {
|
|
277
268
|
setSelectedDetailMarketKey(null);
|
|
278
269
|
}
|
|
270
|
+
setSelectedRowKey(null);
|
|
279
271
|
}, [
|
|
280
272
|
data.selectedRow,
|
|
281
|
-
|
|
282
|
-
effectiveVenueScope,
|
|
283
|
-
focusRegion,
|
|
273
|
+
detailOpen,
|
|
284
274
|
selectedDetailMarketKey,
|
|
285
275
|
selectedRowKey,
|
|
286
|
-
|
|
276
|
+
setDetailOpen,
|
|
287
277
|
setSelectedDetailMarketKey,
|
|
288
278
|
setSelectedRowKey,
|
|
289
|
-
|
|
279
|
+
]);
|
|
280
|
+
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
if (!detailOpen || !data.selectedRow) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (
|
|
287
|
+
selectedDetailMarketKey
|
|
288
|
+
&& data.selectedRow.markets.some(
|
|
289
|
+
(market) => market.key === selectedDetailMarketKey,
|
|
290
|
+
)
|
|
291
|
+
) {
|
|
292
|
+
return;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
setSelectedDetailMarketKey(data.selectedRow.focusMarketKey);
|
|
296
|
+
}, [
|
|
297
|
+
data.selectedRow,
|
|
298
|
+
detailOpen,
|
|
299
|
+
selectedDetailMarketKey,
|
|
300
|
+
setSelectedDetailMarketKey,
|
|
290
301
|
]);
|
|
291
302
|
|
|
292
303
|
useEffect(() => {
|
|
@@ -312,9 +323,8 @@ export function usePredictionMarketsController({
|
|
|
312
323
|
}, [searchFocused]);
|
|
313
324
|
|
|
314
325
|
const focusSearch = useCallback(() => {
|
|
315
|
-
setFocusRegion("list");
|
|
316
326
|
setSearchFocused(true);
|
|
317
|
-
}, [
|
|
327
|
+
}, []);
|
|
318
328
|
|
|
319
329
|
const blurSearch = useCallback(() => {
|
|
320
330
|
setSearchFocused(false);
|
|
@@ -336,52 +346,51 @@ export function usePredictionMarketsController({
|
|
|
336
346
|
[setWatchlist],
|
|
337
347
|
);
|
|
338
348
|
|
|
339
|
-
const
|
|
349
|
+
const setBrowseSelection = useCallback(
|
|
340
350
|
(rowKey: string, options?: { debounceDetail?: boolean }) => {
|
|
341
351
|
setSearchFocused(false);
|
|
342
|
-
setFocusRegion("list");
|
|
343
|
-
const row = data.visibleRows.find((candidate) => candidate.key === rowKey);
|
|
344
|
-
const shouldClear = selectedRowKey === rowKey;
|
|
345
352
|
data.actions.setNextDetailLoadDelay(
|
|
346
|
-
options?.debounceDetail
|
|
347
|
-
? KEYBOARD_DETAIL_LOAD_DELAY_MS
|
|
348
|
-
: 0,
|
|
349
|
-
);
|
|
350
|
-
setSelectedRowKey(shouldClear ? null : rowKey);
|
|
351
|
-
setSelectedDetailMarketKey(
|
|
352
|
-
shouldClear ? null : row?.focusMarketKey ?? null,
|
|
353
|
+
options?.debounceDetail ? KEYBOARD_DETAIL_LOAD_DELAY_MS : 0,
|
|
353
354
|
);
|
|
355
|
+
setSelectedRowKey(rowKey);
|
|
354
356
|
},
|
|
355
|
-
[
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
357
|
+
[data.actions, setSelectedRowKey],
|
|
358
|
+
);
|
|
359
|
+
|
|
360
|
+
const openSelectedRow = useCallback(
|
|
361
|
+
(rowKey: string) => {
|
|
362
|
+
const row = data.visibleRows.find((candidate) => candidate.key === rowKey);
|
|
363
|
+
if (!row) return;
|
|
364
|
+
setSearchFocused(false);
|
|
365
|
+
data.actions.setNextDetailLoadDelay(0);
|
|
366
|
+
setSelectedRowKey(row.key);
|
|
367
|
+
setSelectedDetailMarketKey(row.focusMarketKey);
|
|
368
|
+
setDetailOpen(true);
|
|
369
|
+
},
|
|
370
|
+
[data.actions, data.visibleRows, setSelectedDetailMarketKey, setSelectedRowKey],
|
|
363
371
|
);
|
|
364
372
|
|
|
365
373
|
const selectMarket = useCallback(
|
|
366
374
|
(marketKey: string) => {
|
|
367
375
|
setSearchFocused(false);
|
|
368
|
-
setFocusRegion("detail");
|
|
369
376
|
data.actions.setNextDetailLoadDelay(0);
|
|
370
377
|
const row = data.visibleRows.find((candidate) =>
|
|
371
378
|
candidate.markets.some((market) => market.key === marketKey),
|
|
372
379
|
);
|
|
373
380
|
if (!row) {
|
|
381
|
+
setDetailOpen(false);
|
|
374
382
|
setSelectedRowKey(null);
|
|
375
383
|
setSelectedDetailMarketKey(null);
|
|
376
384
|
return;
|
|
377
385
|
}
|
|
378
386
|
setSelectedRowKey(row.key);
|
|
379
387
|
setSelectedDetailMarketKey(marketKey);
|
|
388
|
+
setDetailOpen(true);
|
|
380
389
|
},
|
|
381
390
|
[
|
|
382
391
|
data.actions,
|
|
383
392
|
data.visibleRows,
|
|
384
|
-
|
|
393
|
+
setDetailOpen,
|
|
385
394
|
setSelectedDetailMarketKey,
|
|
386
395
|
setSelectedRowKey,
|
|
387
396
|
],
|
|
@@ -389,26 +398,34 @@ export function usePredictionMarketsController({
|
|
|
389
398
|
|
|
390
399
|
const clearSelection = useCallback(() => {
|
|
391
400
|
data.actions.setNextDetailLoadDelay(0);
|
|
392
|
-
|
|
401
|
+
setDetailOpen(false);
|
|
393
402
|
setSelectedRowKey(null);
|
|
394
403
|
setSelectedDetailMarketKey(null);
|
|
395
404
|
}, [
|
|
396
405
|
data.actions,
|
|
397
|
-
|
|
406
|
+
setDetailOpen,
|
|
398
407
|
setSelectedDetailMarketKey,
|
|
399
408
|
setSelectedRowKey,
|
|
400
409
|
]);
|
|
401
410
|
|
|
402
|
-
const
|
|
403
|
-
|
|
411
|
+
const closeDetail = useCallback(() => {
|
|
412
|
+
setDetailOpen(false);
|
|
404
413
|
setSearchFocused(false);
|
|
405
|
-
}, [
|
|
414
|
+
}, []);
|
|
406
415
|
|
|
407
|
-
const
|
|
408
|
-
if (!data.
|
|
409
|
-
|
|
416
|
+
const openDetail = useCallback(() => {
|
|
417
|
+
if (!data.selectedRow) return;
|
|
418
|
+
if (
|
|
419
|
+
!selectedDetailMarketKey ||
|
|
420
|
+
!data.selectedRow.markets.some(
|
|
421
|
+
(market) => market.key === selectedDetailMarketKey,
|
|
422
|
+
)
|
|
423
|
+
) {
|
|
424
|
+
setSelectedDetailMarketKey(data.selectedRow.focusMarketKey);
|
|
425
|
+
}
|
|
426
|
+
setDetailOpen(true);
|
|
410
427
|
setSearchFocused(false);
|
|
411
|
-
}, [data.
|
|
428
|
+
}, [data.selectedRow, selectedDetailMarketKey, setSelectedDetailMarketKey]);
|
|
412
429
|
|
|
413
430
|
const setVenue = useCallback(
|
|
414
431
|
(nextVenueScope: string) => {
|
|
@@ -490,6 +507,7 @@ export function usePredictionMarketsController({
|
|
|
490
507
|
name?: string;
|
|
491
508
|
sequence?: string;
|
|
492
509
|
shift?: boolean;
|
|
510
|
+
preventDefault?: () => void;
|
|
493
511
|
stopPropagation?: () => void;
|
|
494
512
|
}) => {
|
|
495
513
|
if (!focused) return;
|
|
@@ -500,40 +518,16 @@ export function usePredictionMarketsController({
|
|
|
500
518
|
if (searchFocused) {
|
|
501
519
|
if (command === "escape") {
|
|
502
520
|
event.stopPropagation?.();
|
|
521
|
+
event.preventDefault?.();
|
|
503
522
|
setSearchFocused(false);
|
|
504
523
|
}
|
|
505
524
|
return;
|
|
506
525
|
}
|
|
507
526
|
|
|
508
|
-
if (
|
|
509
|
-
event.stopPropagation?.();
|
|
510
|
-
focusSearch();
|
|
511
|
-
return;
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
if (command === "escape") {
|
|
515
|
-
event.stopPropagation?.();
|
|
516
|
-
if (focusRegion === "detail") {
|
|
517
|
-
focusList();
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
clearSelection();
|
|
521
|
-
return;
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
if (isEnter && data.selectedSummary) {
|
|
525
|
-
event.stopPropagation?.();
|
|
526
|
-
if (focusRegion === "list") {
|
|
527
|
-
focusDetail();
|
|
528
|
-
} else {
|
|
529
|
-
focusList();
|
|
530
|
-
}
|
|
531
|
-
return;
|
|
532
|
-
}
|
|
533
|
-
|
|
534
|
-
if (focusRegion === "detail") {
|
|
527
|
+
if (detailOpen) {
|
|
535
528
|
if (command === "move-down") {
|
|
536
529
|
event.stopPropagation?.();
|
|
530
|
+
event.preventDefault?.();
|
|
537
531
|
if (detailTab === "overview" && data.sortedOutcomeMarkets.length > 0) {
|
|
538
532
|
cycleDetailOutcome("next");
|
|
539
533
|
} else {
|
|
@@ -544,6 +538,7 @@ export function usePredictionMarketsController({
|
|
|
544
538
|
|
|
545
539
|
if (command === "move-up") {
|
|
546
540
|
event.stopPropagation?.();
|
|
541
|
+
event.preventDefault?.();
|
|
547
542
|
if (detailTab === "overview" && data.sortedOutcomeMarkets.length > 0) {
|
|
548
543
|
cycleDetailOutcome("previous");
|
|
549
544
|
} else {
|
|
@@ -554,39 +549,64 @@ export function usePredictionMarketsController({
|
|
|
554
549
|
|
|
555
550
|
if (command === "previous-category") {
|
|
556
551
|
event.stopPropagation?.();
|
|
552
|
+
event.preventDefault?.();
|
|
557
553
|
setDetailTab(getAdjacentPredictionDetailTab(detailTab, "previous"));
|
|
558
554
|
return;
|
|
559
555
|
}
|
|
560
556
|
|
|
561
557
|
if (command === "next-category") {
|
|
562
558
|
event.stopPropagation?.();
|
|
559
|
+
event.preventDefault?.();
|
|
563
560
|
setDetailTab(getAdjacentPredictionDetailTab(detailTab, "next"));
|
|
564
561
|
return;
|
|
565
562
|
}
|
|
563
|
+
|
|
564
|
+
return;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (command === "search") {
|
|
568
|
+
event.stopPropagation?.();
|
|
569
|
+
event.preventDefault?.();
|
|
570
|
+
focusSearch();
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
if (isEnter && data.selectedRow) {
|
|
575
|
+
event.stopPropagation?.();
|
|
576
|
+
event.preventDefault?.();
|
|
577
|
+
openDetail();
|
|
578
|
+
return;
|
|
566
579
|
}
|
|
567
580
|
|
|
568
581
|
if (command === "move-down") {
|
|
569
582
|
event.stopPropagation?.();
|
|
583
|
+
event.preventDefault?.();
|
|
570
584
|
if (data.visibleRows.length === 0) return;
|
|
571
585
|
const nextIndex =
|
|
572
586
|
data.selectedIndex >= 0
|
|
573
587
|
? Math.min(data.selectedIndex + 1, data.visibleRows.length - 1)
|
|
574
588
|
: 0;
|
|
575
|
-
|
|
589
|
+
setBrowseSelection(data.visibleRows[nextIndex]!.key, {
|
|
590
|
+
debounceDetail: true,
|
|
591
|
+
});
|
|
576
592
|
return;
|
|
577
593
|
}
|
|
578
594
|
|
|
579
595
|
if (command === "move-up") {
|
|
580
596
|
event.stopPropagation?.();
|
|
597
|
+
event.preventDefault?.();
|
|
581
598
|
if (data.visibleRows.length === 0) return;
|
|
582
599
|
const nextIndex =
|
|
583
600
|
data.selectedIndex >= 0 ? Math.max(data.selectedIndex - 1, 0) : 0;
|
|
584
|
-
|
|
601
|
+
setBrowseSelection(data.visibleRows[nextIndex]!.key, {
|
|
602
|
+
debounceDetail: true,
|
|
603
|
+
});
|
|
585
604
|
return;
|
|
586
605
|
}
|
|
587
606
|
|
|
588
607
|
if (!paneSettings.hideTabs && command === "previous-venue-tab") {
|
|
589
608
|
event.stopPropagation?.();
|
|
609
|
+
event.preventDefault?.();
|
|
590
610
|
setVenue(
|
|
591
611
|
getAdjacentPredictionVenueScope(effectiveVenueScope, "previous"),
|
|
592
612
|
);
|
|
@@ -595,24 +615,28 @@ export function usePredictionMarketsController({
|
|
|
595
615
|
|
|
596
616
|
if (!paneSettings.hideTabs && command === "next-venue-tab") {
|
|
597
617
|
event.stopPropagation?.();
|
|
618
|
+
event.preventDefault?.();
|
|
598
619
|
setVenue(getAdjacentPredictionVenueScope(effectiveVenueScope, "next"));
|
|
599
620
|
return;
|
|
600
621
|
}
|
|
601
622
|
|
|
602
623
|
if (command === "previous-category") {
|
|
603
624
|
event.stopPropagation?.();
|
|
625
|
+
event.preventDefault?.();
|
|
604
626
|
selectCategory(getAdjacentPredictionCategoryId(categoryId, "previous"));
|
|
605
627
|
return;
|
|
606
628
|
}
|
|
607
629
|
|
|
608
630
|
if (command === "next-category") {
|
|
609
631
|
event.stopPropagation?.();
|
|
632
|
+
event.preventDefault?.();
|
|
610
633
|
selectCategory(getAdjacentPredictionCategoryId(categoryId, "next"));
|
|
611
634
|
return;
|
|
612
635
|
}
|
|
613
636
|
|
|
614
|
-
if (command === "toggle-watchlist" && data.
|
|
637
|
+
if (command === "toggle-watchlist" && data.selectedRow) {
|
|
615
638
|
event.stopPropagation?.();
|
|
639
|
+
event.preventDefault?.();
|
|
616
640
|
if (data.selectedRow) toggleWatchlist(data.selectedRow);
|
|
617
641
|
return;
|
|
618
642
|
}
|
|
@@ -636,26 +660,23 @@ export function usePredictionMarketsController({
|
|
|
636
660
|
[
|
|
637
661
|
browseTab,
|
|
638
662
|
categoryId,
|
|
639
|
-
clearSelection,
|
|
640
663
|
cycleDetailOutcome,
|
|
664
|
+
detailOpen,
|
|
641
665
|
data.selectedIndex,
|
|
642
666
|
data.selectedRow,
|
|
643
|
-
data.selectedSummary,
|
|
644
667
|
data.sortedOutcomeMarkets.length,
|
|
645
668
|
data.visibleRows,
|
|
646
669
|
detailTab,
|
|
647
670
|
effectiveVenueScope,
|
|
648
|
-
focusDetail,
|
|
649
|
-
focusList,
|
|
650
|
-
focusRegion,
|
|
651
671
|
focusSearch,
|
|
652
672
|
focused,
|
|
673
|
+
openDetail,
|
|
653
674
|
paneSettings.hideTabs,
|
|
654
675
|
scrollDetailBy,
|
|
655
676
|
searchFocused,
|
|
656
677
|
selectBrowseTab,
|
|
657
678
|
selectCategory,
|
|
658
|
-
|
|
679
|
+
setBrowseSelection,
|
|
659
680
|
setVenue,
|
|
660
681
|
toggleWatchlist,
|
|
661
682
|
],
|
|
@@ -676,11 +697,10 @@ export function usePredictionMarketsController({
|
|
|
676
697
|
detail: data.detail,
|
|
677
698
|
detailError: data.detailError,
|
|
678
699
|
detailLoadCount: data.detailLoadCount,
|
|
700
|
+
detailOpen,
|
|
679
701
|
detailScrollRef,
|
|
680
702
|
detailTab,
|
|
681
|
-
detailSplitRatio,
|
|
682
703
|
effectiveVenueScope,
|
|
683
|
-
focusRegion,
|
|
684
704
|
headerScrollRef,
|
|
685
705
|
historyRange,
|
|
686
706
|
hoveredIdx,
|
|
@@ -699,18 +719,18 @@ export function usePredictionMarketsController({
|
|
|
699
719
|
watchlistSet,
|
|
700
720
|
actions: {
|
|
701
721
|
blurSearch,
|
|
722
|
+
closeDetail,
|
|
702
723
|
clearSelection,
|
|
703
|
-
focusDetail,
|
|
704
|
-
focusList,
|
|
705
724
|
focusSearch,
|
|
706
725
|
handleSortHeaderClick,
|
|
726
|
+
openDetail,
|
|
727
|
+
openSelectedRow,
|
|
707
728
|
previewOrder,
|
|
708
729
|
selectBrowseTab,
|
|
709
730
|
selectCategory,
|
|
710
731
|
selectMarket,
|
|
711
|
-
selectRow,
|
|
712
732
|
setDetailTab,
|
|
713
|
-
|
|
733
|
+
setBrowseSelection,
|
|
714
734
|
setHistoryRange,
|
|
715
735
|
setHoveredIdx,
|
|
716
736
|
setSearchQuery,
|
|
@@ -2,6 +2,7 @@ import type { GloomPlugin, GloomPluginContext } from "../../types/plugin";
|
|
|
2
2
|
import { parsePredictionSearchShortcut } from "./navigation";
|
|
3
3
|
import { PredictionMarketsPane } from "./pane";
|
|
4
4
|
import { attachPredictionMarketsPersistence } from "./services/fetch";
|
|
5
|
+
import { predictionMarketsCliCommand } from "./cli";
|
|
5
6
|
import {
|
|
6
7
|
buildPredictionMarketsPaneSettingsDef,
|
|
7
8
|
createPredictionMarketsPaneSettings,
|
|
@@ -26,6 +27,7 @@ export const predictionMarketsPlugin: GloomPlugin = {
|
|
|
26
27
|
description:
|
|
27
28
|
"Browse prediction markets (Polymarket and Kalshi).",
|
|
28
29
|
toggleable: true,
|
|
30
|
+
cliCommands: [predictionMarketsCliCommand],
|
|
29
31
|
panes: [
|
|
30
32
|
{
|
|
31
33
|
id: PANE_ID,
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
type AppConfig,
|
|
8
8
|
type SavedLayout,
|
|
9
9
|
} from "../../types/config";
|
|
10
|
-
import type { PaneDef } from "../../types/plugin";
|
|
10
|
+
import type { CliLaunchRequest, PaneDef } from "../../types/plugin";
|
|
11
11
|
import type { AppSessionSnapshot } from "../../state/session-persistence";
|
|
12
12
|
import { PREDICTION_CATEGORY_OPTIONS, type PredictionCategoryId } from "./categories";
|
|
13
13
|
import { BROWSE_TABS } from "./navigation";
|
|
@@ -48,20 +48,7 @@ function normalizeArg(value: string): string {
|
|
|
48
48
|
return value.trim().toLowerCase();
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
export function
|
|
52
|
-
args: string[],
|
|
53
|
-
): PredictionLaunchIntent | null {
|
|
54
|
-
const [command, ...rest] = args;
|
|
55
|
-
if (!command) return null;
|
|
56
|
-
const normalizedCommand = normalizeArg(command);
|
|
57
|
-
if (
|
|
58
|
-
normalizedCommand !== "predictions" &&
|
|
59
|
-
normalizedCommand !== "prediction-markets" &&
|
|
60
|
-
normalizedCommand !== "pm"
|
|
61
|
-
) {
|
|
62
|
-
return null;
|
|
63
|
-
}
|
|
64
|
-
|
|
51
|
+
export function parsePredictionCommandArgs(args: string[]): PredictionLaunchIntent {
|
|
65
52
|
let venueScope: PredictionVenueScope = "all";
|
|
66
53
|
let categoryId: PredictionCategoryId = "all";
|
|
67
54
|
let browseTab: PredictionBrowseTab = "top";
|
|
@@ -70,7 +57,7 @@ export function parsePredictionLaunchArgs(
|
|
|
70
57
|
let browseExplicit = false;
|
|
71
58
|
const searchTokens: string[] = [];
|
|
72
59
|
|
|
73
|
-
for (const arg of
|
|
60
|
+
for (const arg of args) {
|
|
74
61
|
const normalized = normalizeArg(arg);
|
|
75
62
|
if (!normalized) continue;
|
|
76
63
|
|
|
@@ -100,6 +87,23 @@ export function parsePredictionLaunchArgs(
|
|
|
100
87
|
};
|
|
101
88
|
}
|
|
102
89
|
|
|
90
|
+
export function parsePredictionLaunchArgs(
|
|
91
|
+
args: string[],
|
|
92
|
+
): PredictionLaunchIntent | null {
|
|
93
|
+
const [command, ...rest] = args;
|
|
94
|
+
if (!command) return null;
|
|
95
|
+
const normalizedCommand = normalizeArg(command);
|
|
96
|
+
if (
|
|
97
|
+
normalizedCommand !== "predictions" &&
|
|
98
|
+
normalizedCommand !== "prediction-markets" &&
|
|
99
|
+
normalizedCommand !== "pm"
|
|
100
|
+
) {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return parsePredictionCommandArgs(rest);
|
|
105
|
+
}
|
|
106
|
+
|
|
103
107
|
function syncActiveLayout(
|
|
104
108
|
layouts: SavedLayout[],
|
|
105
109
|
activeLayoutIndex: number,
|
|
@@ -238,3 +242,34 @@ export function applyPredictionLaunchIntentToSessionSnapshot(
|
|
|
238
242
|
savedAt: Date.now(),
|
|
239
243
|
};
|
|
240
244
|
}
|
|
245
|
+
|
|
246
|
+
export function createPredictionLaunchRequest(
|
|
247
|
+
intent: PredictionLaunchIntent,
|
|
248
|
+
): CliLaunchRequest<{ paneInstanceId: string; intent: PredictionLaunchIntent }> {
|
|
249
|
+
return {
|
|
250
|
+
applyConfig(config, env) {
|
|
251
|
+
const result = applyPredictionLaunchIntentToConfig(config, intent, {
|
|
252
|
+
width: Math.max(env.terminalWidth, 120),
|
|
253
|
+
height: Math.max(env.terminalHeight, 40),
|
|
254
|
+
});
|
|
255
|
+
return {
|
|
256
|
+
config: result.config,
|
|
257
|
+
launchState: {
|
|
258
|
+
paneInstanceId: result.paneInstanceId,
|
|
259
|
+
intent,
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
applySessionSnapshot(config, snapshot, launchState) {
|
|
264
|
+
if (!launchState) {
|
|
265
|
+
return applyPredictionLaunchIntentToSessionSnapshot(config, snapshot, PREDICTION_MAIN_INSTANCE_ID, intent);
|
|
266
|
+
}
|
|
267
|
+
return applyPredictionLaunchIntentToSessionSnapshot(
|
|
268
|
+
config,
|
|
269
|
+
snapshot,
|
|
270
|
+
launchState.paneInstanceId,
|
|
271
|
+
launchState.intent,
|
|
272
|
+
);
|
|
273
|
+
},
|
|
274
|
+
};
|
|
275
|
+
}
|
|
@@ -170,13 +170,17 @@ export function getNextPredictionSort(
|
|
|
170
170
|
current: PredictionSortPreference,
|
|
171
171
|
columnId: string,
|
|
172
172
|
): PredictionSortPreference {
|
|
173
|
+
const initialDirection =
|
|
174
|
+
columnId === "ends" || TEXT_SORT_COLUMNS.has(columnId) ? "asc" : "desc";
|
|
175
|
+
const alternateDirection = initialDirection === "asc" ? "desc" : "asc";
|
|
176
|
+
|
|
173
177
|
if (current.columnId !== columnId) {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
178
|
+
return { columnId, direction: initialDirection };
|
|
179
|
+
}
|
|
180
|
+
if (current.direction === initialDirection) {
|
|
181
|
+
return { columnId, direction: alternateDirection };
|
|
177
182
|
}
|
|
178
|
-
|
|
179
|
-
return { columnId: null, direction: "desc" };
|
|
183
|
+
return { columnId: null, direction: initialDirection };
|
|
180
184
|
}
|
|
181
185
|
|
|
182
186
|
export function filterPredictionMarkets(
|