gloomberb 0.6.1 → 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 +1 -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 +48 -27
- 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 +38 -21
- 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 +5 -1
- package/src/components/layout/floating-pane.tsx +30 -2
- package/src/components/layout/header.tsx +10 -7
- 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 +11 -4
- package/src/components/layout/pane-header.tsx +5 -5
- 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/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-chart-tab-switch.test.tsx +3 -4
- 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 +3 -4
- 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 +1 -0
- 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 +74 -10
- package/src/renderers/electrobun/view/key-event.ts +1 -1
- package/src/renderers/electrobun/view/styles.css +331 -0
- package/src/renderers/opentui/chart-surface.tsx +179 -0
- package/src/renderers/opentui/start.tsx +48 -30
- package/src/renderers/opentui/test-utils.tsx +17 -0
- package/src/renderers/opentui/ui-host.tsx +5 -2
- package/src/sources/provider-router.ts +0 -4
- package/src/state/app-context.test.ts +45 -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 +1 -0
- 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
|
@@ -71,12 +71,20 @@ import {
|
|
|
71
71
|
import { notifyGridlockComplete } from "../../plugins/gridlock-notification";
|
|
72
72
|
import { CHART_RENDERER_PREFERENCES } from "../chart/chart-types";
|
|
73
73
|
import {
|
|
74
|
-
buildSections,
|
|
75
74
|
getEmptyState,
|
|
76
75
|
getRowPresentation,
|
|
77
76
|
resolveCommandBarMode,
|
|
78
77
|
truncateText,
|
|
79
78
|
} from "./view-model";
|
|
79
|
+
import {
|
|
80
|
+
buildListRows,
|
|
81
|
+
buildNativeListRows,
|
|
82
|
+
orderListResults,
|
|
83
|
+
type CommandBarListRow,
|
|
84
|
+
type ListScreenState,
|
|
85
|
+
type ResultItem,
|
|
86
|
+
} from "./list-model";
|
|
87
|
+
import { CommandBarConfirmBody } from "./confirm-body";
|
|
80
88
|
import {
|
|
81
89
|
createLocalTickerSearchCandidates,
|
|
82
90
|
normalizeTickerInput,
|
|
@@ -156,49 +164,16 @@ interface CommandBarProps {
|
|
|
156
164
|
onCheckForUpdates?: () => void | Promise<void>;
|
|
157
165
|
}
|
|
158
166
|
|
|
159
|
-
interface ResultItem {
|
|
160
|
-
id: string;
|
|
161
|
-
label: string;
|
|
162
|
-
detail: string;
|
|
163
|
-
category: string;
|
|
164
|
-
kind: "command" | "ticker" | "search" | "theme" | "plugin" | "action" | "info";
|
|
165
|
-
right?: string;
|
|
166
|
-
searchText?: string;
|
|
167
|
-
themeId?: string;
|
|
168
|
-
pluginToggle?: () => void | Promise<void>;
|
|
169
|
-
secondaryAction?: () => void | Promise<void>;
|
|
170
|
-
checked?: boolean;
|
|
171
|
-
current?: boolean;
|
|
172
|
-
disabled?: boolean;
|
|
173
|
-
action: () => void | Promise<void>;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
type ListScreenKind = "root" | "mode" | "picker" | "pane-settings";
|
|
177
|
-
|
|
178
|
-
interface ListScreenState {
|
|
179
|
-
kind: ListScreenKind;
|
|
180
|
-
title: string;
|
|
181
|
-
subtitle?: string;
|
|
182
|
-
query: string;
|
|
183
|
-
selectedIdx: number;
|
|
184
|
-
hoveredIdx: number | null;
|
|
185
|
-
results: ResultItem[];
|
|
186
|
-
searching: boolean;
|
|
187
|
-
emptyLabel: string;
|
|
188
|
-
emptyDetail: string;
|
|
189
|
-
footerLeft: string;
|
|
190
|
-
footerRight: string;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
167
|
type WorkflowStringValues = Record<string, string>;
|
|
194
168
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
169
|
+
const LAYOUT_MOVE_ACTIONS = [
|
|
170
|
+
{ id: "layout-move-left", label: "Move Left", direction: "left", unavailableDetail: "No column to the left" },
|
|
171
|
+
{ id: "layout-move-right", label: "Move Right", direction: "right", unavailableDetail: "No column to the right" },
|
|
172
|
+
{ id: "layout-move-up", label: "Move Up", direction: "above", unavailableDetail: "No pane above" },
|
|
173
|
+
{ id: "layout-move-down", label: "Move Down", direction: "below", unavailableDetail: "No pane below" },
|
|
174
|
+
] as const;
|
|
175
|
+
|
|
176
|
+
type LayoutMoveDirection = (typeof LAYOUT_MOVE_ACTIONS)[number]["direction"];
|
|
202
177
|
|
|
203
178
|
const commandBarLog = debugLog.createLogger("command-bar");
|
|
204
179
|
const COMMAND_BAR_OVERLAY_Z_INDEX = 2_147_483_646;
|
|
@@ -286,40 +261,6 @@ function estimateWorkflowBodyRows(route: CommandBarWorkflowRoute): number {
|
|
|
286
261
|
return introRows + fieldRows + statusRows + 1;
|
|
287
262
|
}
|
|
288
263
|
|
|
289
|
-
function orderListResults(results: ResultItem[]): ResultItem[] {
|
|
290
|
-
return buildSections(results).flatMap((section) => section.items);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
function buildListRows(listState: ListScreenState): CommandBarListRow[] {
|
|
294
|
-
const rows: CommandBarListRow[] = [];
|
|
295
|
-
const sections = buildSections(listState.results);
|
|
296
|
-
let globalIdx = 0;
|
|
297
|
-
sections.forEach((section, sectionIndex) => {
|
|
298
|
-
if (sectionIndex > 0) {
|
|
299
|
-
rows.push({ kind: "spacer", id: `spacer:${sectionIndex}:${section.category}` });
|
|
300
|
-
}
|
|
301
|
-
rows.push({ kind: "heading", id: `heading:${sectionIndex}:${section.category}`, label: section.category });
|
|
302
|
-
for (const item of section.items) {
|
|
303
|
-
rows.push({ kind: "item", item, globalIdx });
|
|
304
|
-
globalIdx += 1;
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
|
-
return rows;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
function buildNativeListRows(listState: ListScreenState, rows: CommandBarListRow[]): CommandBarListRow[] {
|
|
311
|
-
if (listState.searching && rows.length === 0) {
|
|
312
|
-
return [{ kind: "spinner", id: "searching", label: "Searching…" }];
|
|
313
|
-
}
|
|
314
|
-
if (rows.length === 0) {
|
|
315
|
-
return [{ kind: "message", id: "empty", label: listState.emptyLabel }];
|
|
316
|
-
}
|
|
317
|
-
if (listState.searching) {
|
|
318
|
-
return [...rows, { kind: "spinner", id: "searching", label: "Searching…" }];
|
|
319
|
-
}
|
|
320
|
-
return rows;
|
|
321
|
-
}
|
|
322
|
-
|
|
323
264
|
function clampListIndex(index: number, length: number): number {
|
|
324
265
|
return Math.max(0, Math.min(index, Math.max(0, length - 1)));
|
|
325
266
|
}
|
|
@@ -1220,6 +1161,344 @@ export function CommandBar({
|
|
|
1220
1161
|
});
|
|
1221
1162
|
}, [openConfirmRoute]);
|
|
1222
1163
|
|
|
1164
|
+
const buildThemeItems = useCallback((
|
|
1165
|
+
query: string,
|
|
1166
|
+
currentThemeId: string | null | undefined,
|
|
1167
|
+
options?: { onSelectTheme?: (themeId: string) => void },
|
|
1168
|
+
): { items: ResultItem[]; selectedIdx: number } => {
|
|
1169
|
+
let selectedIdx = 0;
|
|
1170
|
+
const filtered = query
|
|
1171
|
+
? getThemeOptions().filter((theme) => (
|
|
1172
|
+
theme.name.toLowerCase().includes(query.toLowerCase())
|
|
1173
|
+
|| theme.id.includes(query.toLowerCase())
|
|
1174
|
+
))
|
|
1175
|
+
: getThemeOptions();
|
|
1176
|
+
const items = filtered.map((theme, index): ResultItem => {
|
|
1177
|
+
const current = theme.id === currentThemeId;
|
|
1178
|
+
if (current) selectedIdx = index;
|
|
1179
|
+
return {
|
|
1180
|
+
id: `theme:${theme.id}`,
|
|
1181
|
+
label: theme.name,
|
|
1182
|
+
detail: theme.description,
|
|
1183
|
+
category: "Themes",
|
|
1184
|
+
kind: "theme",
|
|
1185
|
+
current,
|
|
1186
|
+
themeId: theme.id,
|
|
1187
|
+
action: () => {
|
|
1188
|
+
options?.onSelectTheme?.(theme.id);
|
|
1189
|
+
const nextConfig = {
|
|
1190
|
+
...state.config,
|
|
1191
|
+
theme: theme.id,
|
|
1192
|
+
};
|
|
1193
|
+
dispatch({ type: "SET_THEME", theme: theme.id });
|
|
1194
|
+
void saveConfig(nextConfig);
|
|
1195
|
+
closeAll({ revertThemePreview: false });
|
|
1196
|
+
},
|
|
1197
|
+
};
|
|
1198
|
+
});
|
|
1199
|
+
return { items, selectedIdx };
|
|
1200
|
+
}, [closeAll, dispatch, state.config]);
|
|
1201
|
+
|
|
1202
|
+
const buildPluginItems = useCallback((query: string): ResultItem[] => {
|
|
1203
|
+
const disabledPlugins = state.config.disabledPlugins || [];
|
|
1204
|
+
const toggleable = [...pluginRegistry.allPlugins.values()].filter((plugin) => plugin.toggleable);
|
|
1205
|
+
const filtered = query
|
|
1206
|
+
? toggleable.filter((plugin) => (
|
|
1207
|
+
plugin.name.toLowerCase().includes(query.toLowerCase())
|
|
1208
|
+
|| plugin.id.includes(query.toLowerCase())
|
|
1209
|
+
))
|
|
1210
|
+
: toggleable;
|
|
1211
|
+
return filtered.map((plugin): ResultItem => {
|
|
1212
|
+
const enabled = !disabledPlugins.includes(plugin.id);
|
|
1213
|
+
const toggleAction = () => {
|
|
1214
|
+
dispatch({ type: "TOGGLE_PLUGIN", pluginId: plugin.id });
|
|
1215
|
+
const nextDisabled = enabled
|
|
1216
|
+
? [...disabledPlugins, plugin.id]
|
|
1217
|
+
: disabledPlugins.filter((entry) => entry !== plugin.id);
|
|
1218
|
+
if (enabled) {
|
|
1219
|
+
for (const paneId of pluginRegistry.getPluginPaneIds(plugin.id)) {
|
|
1220
|
+
pluginRegistry.hidePane(paneId);
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
void saveConfig({ ...state.config, disabledPlugins: nextDisabled });
|
|
1224
|
+
};
|
|
1225
|
+
return {
|
|
1226
|
+
id: `plugin:${plugin.id}`,
|
|
1227
|
+
label: plugin.name,
|
|
1228
|
+
detail: plugin.description || "",
|
|
1229
|
+
category: "Plugins",
|
|
1230
|
+
kind: "plugin",
|
|
1231
|
+
checked: enabled,
|
|
1232
|
+
pluginToggle: toggleAction,
|
|
1233
|
+
action: toggleAction,
|
|
1234
|
+
};
|
|
1235
|
+
});
|
|
1236
|
+
}, [dispatch, pluginRegistry, state.config]);
|
|
1237
|
+
|
|
1238
|
+
const buildLayoutItems = useCallback((
|
|
1239
|
+
query: string,
|
|
1240
|
+
options?: { confirmDangerousActions?: boolean },
|
|
1241
|
+
): ResultItem[] => {
|
|
1242
|
+
const currentLayout = state.config.layout;
|
|
1243
|
+
const focusedPane = state.focusedPaneId ? findPaneInstance(currentLayout, state.focusedPaneId) : null;
|
|
1244
|
+
const focusedPaneDef = focusedPane ? pluginRegistry.panes.get(focusedPane.paneId) : null;
|
|
1245
|
+
const dockedPaneIds = getDockedPaneIds(currentLayout);
|
|
1246
|
+
const layoutHistory = state.layoutHistory[state.config.activeLayoutIndex];
|
|
1247
|
+
const layoutSnapshot = JSON.stringify(currentLayout);
|
|
1248
|
+
const canMove = (direction: LayoutMoveDirection) => (
|
|
1249
|
+
!!focusedPane && JSON.stringify(movePaneRelative(currentLayout, focusedPane.instanceId, direction)) !== layoutSnapshot
|
|
1250
|
+
);
|
|
1251
|
+
const layoutItems: ResultItem[] = [];
|
|
1252
|
+
|
|
1253
|
+
if (focusedPane && focusedPaneDef) {
|
|
1254
|
+
const focusedFloating = currentLayout.floating.find((entry) => entry.instanceId === focusedPane.instanceId);
|
|
1255
|
+
layoutItems.push({
|
|
1256
|
+
id: "layout-toggle-mode",
|
|
1257
|
+
label: focusedFloating ? "Dock Pane" : "Float Pane",
|
|
1258
|
+
detail: focusedFloating ? "Return the focused window to the layout" : "Detach the focused pane into a floating window",
|
|
1259
|
+
category: "Focused Pane",
|
|
1260
|
+
kind: "action",
|
|
1261
|
+
action: () => {
|
|
1262
|
+
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
1263
|
+
const nextLayout = focusedFloating
|
|
1264
|
+
? dockPane(currentLayout, focusedPane.instanceId)
|
|
1265
|
+
: floatPane(currentLayout, focusedPane.instanceId, width, height, focusedPaneDef);
|
|
1266
|
+
persistLayoutChange(nextLayout);
|
|
1267
|
+
closeAll({ revertThemePreview: false });
|
|
1268
|
+
},
|
|
1269
|
+
});
|
|
1270
|
+
|
|
1271
|
+
LAYOUT_MOVE_ACTIONS.forEach((entry) => {
|
|
1272
|
+
const available = canMove(entry.direction);
|
|
1273
|
+
layoutItems.push({
|
|
1274
|
+
id: entry.id,
|
|
1275
|
+
label: entry.label,
|
|
1276
|
+
detail: available ? "Reposition the focused pane" : entry.unavailableDetail,
|
|
1277
|
+
category: "Focused Pane",
|
|
1278
|
+
kind: available ? "action" : "info",
|
|
1279
|
+
disabled: !available,
|
|
1280
|
+
action: available
|
|
1281
|
+
? () => {
|
|
1282
|
+
persistLayoutChange(movePaneRelative(currentLayout, focusedPane.instanceId, entry.direction));
|
|
1283
|
+
closeAll({ revertThemePreview: false });
|
|
1284
|
+
}
|
|
1285
|
+
: () => {},
|
|
1286
|
+
});
|
|
1287
|
+
});
|
|
1288
|
+
|
|
1289
|
+
layoutItems.push({
|
|
1290
|
+
id: "layout-swap",
|
|
1291
|
+
label: "Swap With…",
|
|
1292
|
+
detail: dockedPaneIds.length + currentLayout.floating.length > 1
|
|
1293
|
+
? "Choose another pane to swap positions"
|
|
1294
|
+
: "Need at least two panes",
|
|
1295
|
+
category: "Focused Pane",
|
|
1296
|
+
kind: "action",
|
|
1297
|
+
disabled: dockedPaneIds.length + currentLayout.floating.length <= 1,
|
|
1298
|
+
action: () => {
|
|
1299
|
+
const pickerOptions = [
|
|
1300
|
+
...dockedPaneIds,
|
|
1301
|
+
...currentLayout.floating.map((entry) => entry.instanceId),
|
|
1302
|
+
]
|
|
1303
|
+
.filter((paneId) => paneId !== focusedPane.instanceId)
|
|
1304
|
+
.map((paneId) => {
|
|
1305
|
+
const instance = findPaneInstance(currentLayout, paneId)!;
|
|
1306
|
+
const isFloating = currentLayout.floating.some((entry) => entry.instanceId === paneId);
|
|
1307
|
+
return {
|
|
1308
|
+
id: paneId,
|
|
1309
|
+
label: instance.title || pluginRegistry.panes.get(instance.paneId)?.name || instance.paneId,
|
|
1310
|
+
detail: isFloating ? "Floating window" : "Docked pane",
|
|
1311
|
+
description: isFloating ? "Floating window" : "Docked pane",
|
|
1312
|
+
};
|
|
1313
|
+
});
|
|
1314
|
+
if (pickerOptions.length === 0) return;
|
|
1315
|
+
openPickerRoute({
|
|
1316
|
+
kind: "picker",
|
|
1317
|
+
pickerId: "layout-swap",
|
|
1318
|
+
title: "Swap With…",
|
|
1319
|
+
query: "",
|
|
1320
|
+
selectedIdx: 0,
|
|
1321
|
+
hoveredIdx: null,
|
|
1322
|
+
options: pickerOptions,
|
|
1323
|
+
payload: { sourcePaneId: focusedPane.instanceId },
|
|
1324
|
+
});
|
|
1325
|
+
},
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
layoutItems.push({
|
|
1329
|
+
id: "layout-duplicate",
|
|
1330
|
+
label: "Duplicate Pane",
|
|
1331
|
+
detail: "Create another instance next to the focused pane",
|
|
1332
|
+
category: "Focused Pane",
|
|
1333
|
+
kind: "action",
|
|
1334
|
+
action: () => {
|
|
1335
|
+
duplicatePane(focusedPane.instanceId);
|
|
1336
|
+
closeAll({ revertThemePreview: false });
|
|
1337
|
+
},
|
|
1338
|
+
});
|
|
1339
|
+
|
|
1340
|
+
layoutItems.push({
|
|
1341
|
+
id: "layout-close-pane",
|
|
1342
|
+
label: "Close Pane",
|
|
1343
|
+
detail: "Remove the focused pane from the layout",
|
|
1344
|
+
category: "Focused Pane",
|
|
1345
|
+
kind: "action",
|
|
1346
|
+
action: options?.confirmDangerousActions
|
|
1347
|
+
? () => {
|
|
1348
|
+
openInlineConfirm({
|
|
1349
|
+
confirmId: "layout-close-pane",
|
|
1350
|
+
title: "Close Pane",
|
|
1351
|
+
body: [`Close "${focusedPane.title || focusedPaneDef.name || focusedPane.instanceId}"?`],
|
|
1352
|
+
confirmLabel: "Close Pane",
|
|
1353
|
+
cancelLabel: "Back",
|
|
1354
|
+
tone: "danger",
|
|
1355
|
+
onConfirm: () => {
|
|
1356
|
+
persistLayoutChange(removePane(currentLayout, focusedPane.instanceId));
|
|
1357
|
+
},
|
|
1358
|
+
});
|
|
1359
|
+
}
|
|
1360
|
+
: () => {
|
|
1361
|
+
persistLayoutChange(removePane(currentLayout, focusedPane.instanceId));
|
|
1362
|
+
closeAll({ revertThemePreview: false });
|
|
1363
|
+
},
|
|
1364
|
+
});
|
|
1365
|
+
} else {
|
|
1366
|
+
layoutItems.push({
|
|
1367
|
+
id: "layout-no-focused-pane",
|
|
1368
|
+
label: "No focused pane",
|
|
1369
|
+
detail: "Focus a pane to show pane-specific layout actions",
|
|
1370
|
+
category: "Focused Pane",
|
|
1371
|
+
kind: "info",
|
|
1372
|
+
action: () => {},
|
|
1373
|
+
});
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
layoutItems.push({
|
|
1377
|
+
id: "layout-undo",
|
|
1378
|
+
label: "Undo Layout Change",
|
|
1379
|
+
detail: (layoutHistory?.past.length ?? 0) > 0 ? "Restore the previous layout state" : "No previous layout state",
|
|
1380
|
+
category: "Current Layout",
|
|
1381
|
+
kind: "action",
|
|
1382
|
+
disabled: (layoutHistory?.past.length ?? 0) === 0,
|
|
1383
|
+
action: () => {
|
|
1384
|
+
if ((layoutHistory?.past.length ?? 0) === 0) return;
|
|
1385
|
+
dispatch({ type: "UNDO_LAYOUT" });
|
|
1386
|
+
closeAll({ revertThemePreview: false });
|
|
1387
|
+
},
|
|
1388
|
+
});
|
|
1389
|
+
layoutItems.push({
|
|
1390
|
+
id: "layout-redo",
|
|
1391
|
+
label: "Redo Layout Change",
|
|
1392
|
+
detail: (layoutHistory?.future.length ?? 0) > 0 ? "Reapply the next layout state" : "No later layout state",
|
|
1393
|
+
category: "Current Layout",
|
|
1394
|
+
kind: "action",
|
|
1395
|
+
disabled: (layoutHistory?.future.length ?? 0) === 0,
|
|
1396
|
+
action: () => {
|
|
1397
|
+
if ((layoutHistory?.future.length ?? 0) === 0) return;
|
|
1398
|
+
dispatch({ type: "REDO_LAYOUT" });
|
|
1399
|
+
closeAll({ revertThemePreview: false });
|
|
1400
|
+
},
|
|
1401
|
+
});
|
|
1402
|
+
layoutItems.push({
|
|
1403
|
+
id: "layout-reset",
|
|
1404
|
+
label: "Reset Current Layout",
|
|
1405
|
+
detail: "Restore the default two-pane layout",
|
|
1406
|
+
category: "Current Layout",
|
|
1407
|
+
kind: "action",
|
|
1408
|
+
action: options?.confirmDangerousActions
|
|
1409
|
+
? () => {
|
|
1410
|
+
openInlineConfirm({
|
|
1411
|
+
confirmId: "layout-reset",
|
|
1412
|
+
title: "Reset Current Layout",
|
|
1413
|
+
body: ["Reset the current layout to the default two-pane arrangement?"],
|
|
1414
|
+
confirmLabel: "Reset Layout",
|
|
1415
|
+
cancelLabel: "Back",
|
|
1416
|
+
tone: "danger",
|
|
1417
|
+
onConfirm: () => {
|
|
1418
|
+
persistLayoutChange(cloneLayout(DEFAULT_LAYOUT));
|
|
1419
|
+
},
|
|
1420
|
+
});
|
|
1421
|
+
}
|
|
1422
|
+
: () => {
|
|
1423
|
+
persistLayoutChange(cloneLayout(DEFAULT_LAYOUT));
|
|
1424
|
+
closeAll({ revertThemePreview: false });
|
|
1425
|
+
},
|
|
1426
|
+
});
|
|
1427
|
+
layoutItems.push({
|
|
1428
|
+
id: "layout-gridlock",
|
|
1429
|
+
label: "Gridlock All Windows",
|
|
1430
|
+
detail: currentLayout.floating.length > 0
|
|
1431
|
+
? "Infer a tiled layout from the current window positions"
|
|
1432
|
+
: "Retile all panes from their current arrangement",
|
|
1433
|
+
category: "Current Layout",
|
|
1434
|
+
kind: "action",
|
|
1435
|
+
action: () => {
|
|
1436
|
+
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
1437
|
+
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
1438
|
+
notifyGridlockRevert();
|
|
1439
|
+
closeAll({ revertThemePreview: false });
|
|
1440
|
+
},
|
|
1441
|
+
});
|
|
1442
|
+
layoutItems.push({
|
|
1443
|
+
id: "layout-rename",
|
|
1444
|
+
label: "Rename Layout",
|
|
1445
|
+
detail: "Change the current saved layout name",
|
|
1446
|
+
category: "Current Layout",
|
|
1447
|
+
kind: "action",
|
|
1448
|
+
action: () => openBuiltInWorkflow("rename-layout"),
|
|
1449
|
+
});
|
|
1450
|
+
layoutItems.push({
|
|
1451
|
+
id: "layout-duplicate-layout",
|
|
1452
|
+
label: "Duplicate Layout",
|
|
1453
|
+
detail: "Create a copy of the current layout",
|
|
1454
|
+
category: "Current Layout",
|
|
1455
|
+
kind: "action",
|
|
1456
|
+
action: () => {
|
|
1457
|
+
dispatch({ type: "DUPLICATE_LAYOUT", index: state.config.activeLayoutIndex });
|
|
1458
|
+
closeAll({ revertThemePreview: false });
|
|
1459
|
+
},
|
|
1460
|
+
});
|
|
1461
|
+
layoutItems.push({
|
|
1462
|
+
id: "layout-new",
|
|
1463
|
+
label: "New Layout",
|
|
1464
|
+
detail: "Create a fresh saved layout",
|
|
1465
|
+
category: "Current Layout",
|
|
1466
|
+
kind: "action",
|
|
1467
|
+
action: () => openBuiltInWorkflow("new-layout"),
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
state.config.layouts.forEach((savedLayout, index) => {
|
|
1471
|
+
layoutItems.push({
|
|
1472
|
+
id: `layout-switch:${index}`,
|
|
1473
|
+
label: savedLayout.name,
|
|
1474
|
+
detail: index === state.config.activeLayoutIndex ? "Current layout" : "Switch to this saved layout",
|
|
1475
|
+
right: getLayoutPreview(savedLayout.layout),
|
|
1476
|
+
category: "Saved Layouts",
|
|
1477
|
+
kind: "action",
|
|
1478
|
+
current: index === state.config.activeLayoutIndex,
|
|
1479
|
+
action: () => {
|
|
1480
|
+
dispatch({ type: "SWITCH_LAYOUT", index });
|
|
1481
|
+
closeAll({ revertThemePreview: false });
|
|
1482
|
+
},
|
|
1483
|
+
});
|
|
1484
|
+
});
|
|
1485
|
+
|
|
1486
|
+
return query
|
|
1487
|
+
? fuzzyFilter(layoutItems, query, (item) => `${item.label} ${item.detail} ${item.right || ""}`)
|
|
1488
|
+
: layoutItems;
|
|
1489
|
+
}, [
|
|
1490
|
+
closeAll,
|
|
1491
|
+
dispatch,
|
|
1492
|
+
duplicatePane,
|
|
1493
|
+
notifyGridlockRevert,
|
|
1494
|
+
openBuiltInWorkflow,
|
|
1495
|
+
openInlineConfirm,
|
|
1496
|
+
openPickerRoute,
|
|
1497
|
+
persistLayoutChange,
|
|
1498
|
+
pluginRegistry,
|
|
1499
|
+
state,
|
|
1500
|
+
]);
|
|
1501
|
+
|
|
1223
1502
|
const resolvePluginCommandConfirm = useCallback((command: CommandDef) => {
|
|
1224
1503
|
const context = {
|
|
1225
1504
|
config: state.config,
|
|
@@ -1987,10 +2266,15 @@ export function CommandBar({
|
|
|
1987
2266
|
activeCollectionId,
|
|
1988
2267
|
buildSharedWorkflowDeps(),
|
|
1989
2268
|
);
|
|
1990
|
-
|
|
2269
|
+
const createOptions = {
|
|
1991
2270
|
arg: trimmedList,
|
|
1992
2271
|
symbols,
|
|
1993
|
-
}
|
|
2272
|
+
};
|
|
2273
|
+
if (template.canCreate && !template.canCreate(getPaneTemplateContext(), createOptions)) {
|
|
2274
|
+
openPaneTemplateWorkflow(template, { arg: trimmedArg });
|
|
2275
|
+
return;
|
|
2276
|
+
}
|
|
2277
|
+
await openPaneTemplateDirect(template, createOptions);
|
|
1994
2278
|
} catch {
|
|
1995
2279
|
openPaneTemplateWorkflow(template, { arg: trimmedArg });
|
|
1996
2280
|
}
|
|
@@ -2412,7 +2696,7 @@ export function CommandBar({
|
|
|
2412
2696
|
switch (command.id) {
|
|
2413
2697
|
case "help":
|
|
2414
2698
|
closeAll({ revertThemePreview: false });
|
|
2415
|
-
pluginRegistry.
|
|
2699
|
+
pluginRegistry.showPane("help");
|
|
2416
2700
|
return;
|
|
2417
2701
|
case "pane-settings":
|
|
2418
2702
|
if (state.focusedPaneId) openPaneSettingsRoute(state.focusedPaneId);
|
|
@@ -2959,321 +3243,18 @@ export function CommandBar({
|
|
|
2959
3243
|
) {
|
|
2960
3244
|
items.push(shortcutItem);
|
|
2961
3245
|
} else if (match && match.command.id === "plugins") {
|
|
2962
|
-
|
|
2963
|
-
const toggleable = [...pluginRegistry.allPlugins.values()].filter((plugin) => plugin.toggleable);
|
|
2964
|
-
const filtered = match.arg
|
|
2965
|
-
? toggleable.filter((plugin) => (
|
|
2966
|
-
plugin.name.toLowerCase().includes(match.arg.toLowerCase())
|
|
2967
|
-
|| plugin.id.includes(match.arg.toLowerCase())
|
|
2968
|
-
))
|
|
2969
|
-
: toggleable;
|
|
2970
|
-
for (const plugin of filtered) {
|
|
2971
|
-
const enabled = !disabledPlugins.includes(plugin.id);
|
|
2972
|
-
const toggleAction = () => {
|
|
2973
|
-
dispatch({ type: "TOGGLE_PLUGIN", pluginId: plugin.id });
|
|
2974
|
-
const nextDisabled = enabled
|
|
2975
|
-
? [...disabledPlugins, plugin.id]
|
|
2976
|
-
: disabledPlugins.filter((entry) => entry !== plugin.id);
|
|
2977
|
-
if (enabled) {
|
|
2978
|
-
for (const paneId of pluginRegistry.getPluginPaneIds(plugin.id)) {
|
|
2979
|
-
pluginRegistry.hideWidget(paneId);
|
|
2980
|
-
}
|
|
2981
|
-
}
|
|
2982
|
-
void saveConfig({ ...state.config, disabledPlugins: nextDisabled });
|
|
2983
|
-
};
|
|
2984
|
-
items.push({
|
|
2985
|
-
id: `plugin:${plugin.id}`,
|
|
2986
|
-
label: plugin.name,
|
|
2987
|
-
detail: plugin.description || "",
|
|
2988
|
-
category: "Plugins",
|
|
2989
|
-
kind: "plugin",
|
|
2990
|
-
checked: enabled,
|
|
2991
|
-
pluginToggle: toggleAction,
|
|
2992
|
-
action: toggleAction,
|
|
2993
|
-
});
|
|
2994
|
-
}
|
|
3246
|
+
items.push(...buildPluginItems(match.arg));
|
|
2995
3247
|
} else if (match && match.command.id === "layout") {
|
|
2996
|
-
|
|
2997
|
-
const focusedPane = state.focusedPaneId ? findPaneInstance(currentLayout, state.focusedPaneId) : null;
|
|
2998
|
-
const focusedPaneDef = focusedPane ? pluginRegistry.panes.get(focusedPane.paneId) : null;
|
|
2999
|
-
const dockedPaneIds = getDockedPaneIds(currentLayout);
|
|
3000
|
-
const focusedFloating = focusedPane ? currentLayout.floating.find((entry) => entry.instanceId === focusedPane.instanceId) : null;
|
|
3001
|
-
const layoutHistory = state.layoutHistory[state.config.activeLayoutIndex];
|
|
3002
|
-
const layoutSnapshot = JSON.stringify(currentLayout);
|
|
3003
|
-
const canMove = (direction: "left" | "right" | "above" | "below") => (
|
|
3004
|
-
!!focusedPane && JSON.stringify(movePaneRelative(currentLayout, focusedPane.instanceId, direction)) !== layoutSnapshot
|
|
3005
|
-
);
|
|
3006
|
-
const layoutItems: ResultItem[] = [];
|
|
3007
|
-
|
|
3008
|
-
if (focusedPane && focusedPaneDef) {
|
|
3009
|
-
layoutItems.push({
|
|
3010
|
-
id: "layout-toggle-mode",
|
|
3011
|
-
label: focusedFloating ? "Dock Pane" : "Float Pane",
|
|
3012
|
-
detail: focusedFloating ? "Return the focused window to the layout" : "Detach the focused pane into a floating window",
|
|
3013
|
-
category: "Focused Pane",
|
|
3014
|
-
kind: "action",
|
|
3015
|
-
action: () => {
|
|
3016
|
-
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
3017
|
-
const nextLayout = focusedFloating
|
|
3018
|
-
? dockPane(currentLayout, focusedPane.instanceId)
|
|
3019
|
-
: floatPane(currentLayout, focusedPane.instanceId, width, height, focusedPaneDef);
|
|
3020
|
-
persistLayoutChange(nextLayout);
|
|
3021
|
-
closeAll({ revertThemePreview: false });
|
|
3022
|
-
},
|
|
3023
|
-
});
|
|
3024
|
-
|
|
3025
|
-
const moveActions: Array<{
|
|
3026
|
-
id: string;
|
|
3027
|
-
label: string;
|
|
3028
|
-
direction: "left" | "right" | "above" | "below";
|
|
3029
|
-
available: boolean;
|
|
3030
|
-
unavailableDetail: string;
|
|
3031
|
-
}> = [
|
|
3032
|
-
{ id: "layout-move-left", label: "Move Left", direction: "left", available: canMove("left"), unavailableDetail: "No column to the left" },
|
|
3033
|
-
{ id: "layout-move-right", label: "Move Right", direction: "right", available: canMove("right"), unavailableDetail: "No column to the right" },
|
|
3034
|
-
{ id: "layout-move-up", label: "Move Up", direction: "above", available: canMove("above"), unavailableDetail: "No pane above" },
|
|
3035
|
-
{ id: "layout-move-down", label: "Move Down", direction: "below", available: canMove("below"), unavailableDetail: "No pane below" },
|
|
3036
|
-
];
|
|
3037
|
-
|
|
3038
|
-
moveActions.forEach((entry) => {
|
|
3039
|
-
layoutItems.push({
|
|
3040
|
-
id: entry.id,
|
|
3041
|
-
label: entry.label,
|
|
3042
|
-
detail: entry.available ? "Reposition the focused pane" : entry.unavailableDetail,
|
|
3043
|
-
category: "Focused Pane",
|
|
3044
|
-
kind: entry.available ? "action" : "info",
|
|
3045
|
-
disabled: !entry.available,
|
|
3046
|
-
action: entry.available
|
|
3047
|
-
? () => {
|
|
3048
|
-
persistLayoutChange(movePaneRelative(currentLayout, focusedPane.instanceId, entry.direction));
|
|
3049
|
-
closeAll({ revertThemePreview: false });
|
|
3050
|
-
}
|
|
3051
|
-
: () => {},
|
|
3052
|
-
});
|
|
3053
|
-
});
|
|
3054
|
-
|
|
3055
|
-
layoutItems.push({
|
|
3056
|
-
id: "layout-swap",
|
|
3057
|
-
label: "Swap With…",
|
|
3058
|
-
detail: dockedPaneIds.length + currentLayout.floating.length > 1
|
|
3059
|
-
? "Choose another pane to swap positions"
|
|
3060
|
-
: "Need at least two panes",
|
|
3061
|
-
category: "Focused Pane",
|
|
3062
|
-
kind: "action",
|
|
3063
|
-
disabled: dockedPaneIds.length + currentLayout.floating.length <= 1,
|
|
3064
|
-
action: () => {
|
|
3065
|
-
const options = [
|
|
3066
|
-
...dockedPaneIds,
|
|
3067
|
-
...currentLayout.floating.map((entry) => entry.instanceId),
|
|
3068
|
-
]
|
|
3069
|
-
.filter((paneId) => paneId !== focusedPane.instanceId)
|
|
3070
|
-
.map((paneId) => {
|
|
3071
|
-
const instance = findPaneInstance(currentLayout, paneId)!;
|
|
3072
|
-
const isFloating = currentLayout.floating.some((entry) => entry.instanceId === paneId);
|
|
3073
|
-
return {
|
|
3074
|
-
id: paneId,
|
|
3075
|
-
label: instance.title || pluginRegistry.panes.get(instance.paneId)?.name || instance.paneId,
|
|
3076
|
-
detail: isFloating ? "Floating window" : "Docked pane",
|
|
3077
|
-
description: isFloating ? "Floating window" : "Docked pane",
|
|
3078
|
-
};
|
|
3079
|
-
});
|
|
3080
|
-
if (options.length === 0) return;
|
|
3081
|
-
openPickerRoute({
|
|
3082
|
-
kind: "picker",
|
|
3083
|
-
pickerId: "layout-swap",
|
|
3084
|
-
title: "Swap With…",
|
|
3085
|
-
query: "",
|
|
3086
|
-
selectedIdx: 0,
|
|
3087
|
-
hoveredIdx: null,
|
|
3088
|
-
options,
|
|
3089
|
-
payload: { sourcePaneId: focusedPane.instanceId },
|
|
3090
|
-
});
|
|
3091
|
-
},
|
|
3092
|
-
});
|
|
3093
|
-
|
|
3094
|
-
layoutItems.push({
|
|
3095
|
-
id: "layout-duplicate",
|
|
3096
|
-
label: "Duplicate Pane",
|
|
3097
|
-
detail: "Create another instance next to the focused pane",
|
|
3098
|
-
category: "Focused Pane",
|
|
3099
|
-
kind: "action",
|
|
3100
|
-
action: () => {
|
|
3101
|
-
duplicatePane(focusedPane.instanceId);
|
|
3102
|
-
closeAll({ revertThemePreview: false });
|
|
3103
|
-
},
|
|
3104
|
-
});
|
|
3105
|
-
|
|
3106
|
-
layoutItems.push({
|
|
3107
|
-
id: "layout-close-pane",
|
|
3108
|
-
label: "Close Pane",
|
|
3109
|
-
detail: "Remove the focused pane from the layout",
|
|
3110
|
-
category: "Focused Pane",
|
|
3111
|
-
kind: "action",
|
|
3112
|
-
action: () => {
|
|
3113
|
-
openInlineConfirm({
|
|
3114
|
-
confirmId: "layout-close-pane",
|
|
3115
|
-
title: "Close Pane",
|
|
3116
|
-
body: [`Close "${focusedPane.title || focusedPaneDef.name || focusedPane.instanceId}"?`],
|
|
3117
|
-
confirmLabel: "Close Pane",
|
|
3118
|
-
cancelLabel: "Back",
|
|
3119
|
-
tone: "danger",
|
|
3120
|
-
onConfirm: () => {
|
|
3121
|
-
persistLayoutChange(removePane(currentLayout, focusedPane.instanceId));
|
|
3122
|
-
},
|
|
3123
|
-
});
|
|
3124
|
-
},
|
|
3125
|
-
});
|
|
3126
|
-
} else {
|
|
3127
|
-
layoutItems.push({
|
|
3128
|
-
id: "layout-no-focused-pane",
|
|
3129
|
-
label: "No focused pane",
|
|
3130
|
-
detail: "Focus a pane to show pane-specific layout actions",
|
|
3131
|
-
category: "Focused Pane",
|
|
3132
|
-
kind: "info",
|
|
3133
|
-
action: () => {},
|
|
3134
|
-
});
|
|
3135
|
-
}
|
|
3136
|
-
|
|
3137
|
-
layoutItems.push({
|
|
3138
|
-
id: "layout-undo",
|
|
3139
|
-
label: "Undo Layout Change",
|
|
3140
|
-
detail: (layoutHistory?.past.length ?? 0) > 0 ? "Restore the previous layout state" : "No previous layout state",
|
|
3141
|
-
category: "Current Layout",
|
|
3142
|
-
kind: "action",
|
|
3143
|
-
disabled: (layoutHistory?.past.length ?? 0) === 0,
|
|
3144
|
-
action: () => {
|
|
3145
|
-
if ((layoutHistory?.past.length ?? 0) === 0) return;
|
|
3146
|
-
dispatch({ type: "UNDO_LAYOUT" });
|
|
3147
|
-
closeAll({ revertThemePreview: false });
|
|
3148
|
-
},
|
|
3149
|
-
});
|
|
3150
|
-
layoutItems.push({
|
|
3151
|
-
id: "layout-redo",
|
|
3152
|
-
label: "Redo Layout Change",
|
|
3153
|
-
detail: (layoutHistory?.future.length ?? 0) > 0 ? "Reapply the next layout state" : "No later layout state",
|
|
3154
|
-
category: "Current Layout",
|
|
3155
|
-
kind: "action",
|
|
3156
|
-
disabled: (layoutHistory?.future.length ?? 0) === 0,
|
|
3157
|
-
action: () => {
|
|
3158
|
-
if ((layoutHistory?.future.length ?? 0) === 0) return;
|
|
3159
|
-
dispatch({ type: "REDO_LAYOUT" });
|
|
3160
|
-
closeAll({ revertThemePreview: false });
|
|
3161
|
-
},
|
|
3162
|
-
});
|
|
3163
|
-
layoutItems.push({
|
|
3164
|
-
id: "layout-reset",
|
|
3165
|
-
label: "Reset Current Layout",
|
|
3166
|
-
detail: "Restore the default two-pane layout",
|
|
3167
|
-
category: "Current Layout",
|
|
3168
|
-
kind: "action",
|
|
3169
|
-
action: () => {
|
|
3170
|
-
openInlineConfirm({
|
|
3171
|
-
confirmId: "layout-reset",
|
|
3172
|
-
title: "Reset Current Layout",
|
|
3173
|
-
body: ["Reset the current layout to the default two-pane arrangement?"],
|
|
3174
|
-
confirmLabel: "Reset Layout",
|
|
3175
|
-
cancelLabel: "Back",
|
|
3176
|
-
tone: "danger",
|
|
3177
|
-
onConfirm: () => {
|
|
3178
|
-
persistLayoutChange(cloneLayout(DEFAULT_LAYOUT));
|
|
3179
|
-
},
|
|
3180
|
-
});
|
|
3181
|
-
},
|
|
3182
|
-
});
|
|
3183
|
-
layoutItems.push({
|
|
3184
|
-
id: "layout-gridlock",
|
|
3185
|
-
label: "Gridlock All Windows",
|
|
3186
|
-
detail: currentLayout.floating.length > 0
|
|
3187
|
-
? "Infer a tiled layout from the current window positions"
|
|
3188
|
-
: "Retile all panes from their current arrangement",
|
|
3189
|
-
category: "Current Layout",
|
|
3190
|
-
kind: "action",
|
|
3191
|
-
action: () => {
|
|
3192
|
-
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
3193
|
-
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
3194
|
-
notifyGridlockRevert();
|
|
3195
|
-
closeAll({ revertThemePreview: false });
|
|
3196
|
-
},
|
|
3197
|
-
});
|
|
3198
|
-
layoutItems.push({
|
|
3199
|
-
id: "layout-rename",
|
|
3200
|
-
label: "Rename Layout",
|
|
3201
|
-
detail: "Change the current saved layout name",
|
|
3202
|
-
category: "Current Layout",
|
|
3203
|
-
kind: "action",
|
|
3204
|
-
action: () => openBuiltInWorkflow("rename-layout"),
|
|
3205
|
-
});
|
|
3206
|
-
layoutItems.push({
|
|
3207
|
-
id: "layout-duplicate-layout",
|
|
3208
|
-
label: "Duplicate Layout",
|
|
3209
|
-
detail: "Create a copy of the current layout",
|
|
3210
|
-
category: "Current Layout",
|
|
3211
|
-
kind: "action",
|
|
3212
|
-
action: () => {
|
|
3213
|
-
dispatch({ type: "DUPLICATE_LAYOUT", index: state.config.activeLayoutIndex });
|
|
3214
|
-
closeAll({ revertThemePreview: false });
|
|
3215
|
-
},
|
|
3216
|
-
});
|
|
3217
|
-
layoutItems.push({
|
|
3218
|
-
id: "layout-new",
|
|
3219
|
-
label: "New Layout",
|
|
3220
|
-
detail: "Create a fresh saved layout",
|
|
3221
|
-
category: "Current Layout",
|
|
3222
|
-
kind: "action",
|
|
3223
|
-
action: () => openBuiltInWorkflow("new-layout"),
|
|
3224
|
-
});
|
|
3225
|
-
|
|
3226
|
-
state.config.layouts.forEach((savedLayout, index) => {
|
|
3227
|
-
layoutItems.push({
|
|
3228
|
-
id: `layout-switch:${index}`,
|
|
3229
|
-
label: savedLayout.name,
|
|
3230
|
-
detail: index === state.config.activeLayoutIndex ? "Current layout" : "Switch to this saved layout",
|
|
3231
|
-
right: getLayoutPreview(savedLayout.layout),
|
|
3232
|
-
category: "Saved Layouts",
|
|
3233
|
-
kind: "action",
|
|
3234
|
-
current: index === state.config.activeLayoutIndex,
|
|
3235
|
-
action: () => {
|
|
3236
|
-
dispatch({ type: "SWITCH_LAYOUT", index });
|
|
3237
|
-
closeAll({ revertThemePreview: false });
|
|
3238
|
-
},
|
|
3239
|
-
});
|
|
3240
|
-
});
|
|
3241
|
-
|
|
3242
|
-
items.push(...(match.arg
|
|
3243
|
-
? fuzzyFilter(layoutItems, match.arg, (item) => `${item.label} ${item.detail} ${item.right || ""}`)
|
|
3244
|
-
: layoutItems));
|
|
3248
|
+
items.push(...buildLayoutItems(match.arg, { confirmDangerousActions: true }));
|
|
3245
3249
|
} else if (match && match.command.id === "theme") {
|
|
3246
3250
|
const savedThemeId = rootThemeBaseIdRef.current ?? state.config.theme;
|
|
3247
|
-
const
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
3251
|
-
|| theme.id.includes(match.arg.toLowerCase())
|
|
3252
|
-
))
|
|
3253
|
-
: themeOptions;
|
|
3254
|
-
filtered.forEach((theme, index) => {
|
|
3255
|
-
const isSaved = theme.id === savedThemeId;
|
|
3256
|
-
if (isSaved) initialIdx = index;
|
|
3257
|
-
items.push({
|
|
3258
|
-
id: `theme:${theme.id}`,
|
|
3259
|
-
label: theme.name,
|
|
3260
|
-
detail: theme.description,
|
|
3261
|
-
category: "Themes",
|
|
3262
|
-
kind: "theme",
|
|
3263
|
-
current: isSaved,
|
|
3264
|
-
themeId: theme.id,
|
|
3265
|
-
action: () => {
|
|
3266
|
-
rootThemeBaseIdRef.current = theme.id;
|
|
3267
|
-
const nextConfig = {
|
|
3268
|
-
...state.config,
|
|
3269
|
-
theme: theme.id,
|
|
3270
|
-
};
|
|
3271
|
-
dispatch({ type: "SET_THEME", theme: theme.id });
|
|
3272
|
-
void saveConfig(nextConfig);
|
|
3273
|
-
closeAll({ revertThemePreview: false });
|
|
3274
|
-
},
|
|
3275
|
-
});
|
|
3251
|
+
const themeResult = buildThemeItems(match.arg, savedThemeId, {
|
|
3252
|
+
onSelectTheme: (themeId) => {
|
|
3253
|
+
rootThemeBaseIdRef.current = themeId;
|
|
3254
|
+
},
|
|
3276
3255
|
});
|
|
3256
|
+
initialIdx = themeResult.selectedIdx;
|
|
3257
|
+
items.push(...themeResult.items);
|
|
3277
3258
|
} else if (match && match.command.id === "security-description") {
|
|
3278
3259
|
if (shortcutItem) {
|
|
3279
3260
|
items.push(shortcutItem);
|
|
@@ -3341,19 +3322,15 @@ export function CommandBar({
|
|
|
3341
3322
|
activeTickerData,
|
|
3342
3323
|
activeTickerSymbol,
|
|
3343
3324
|
availableCommands,
|
|
3344
|
-
|
|
3325
|
+
buildLayoutItems,
|
|
3326
|
+
buildPluginItems,
|
|
3327
|
+
buildThemeItems,
|
|
3345
3328
|
createPluginCommandItem,
|
|
3346
3329
|
currentRoute,
|
|
3347
|
-
dispatch,
|
|
3348
|
-
duplicatePane,
|
|
3349
3330
|
executeCollectionCommand,
|
|
3350
3331
|
localTickerSearchResultItems,
|
|
3351
3332
|
nonShortcutPaneTemplateItems,
|
|
3352
|
-
notifyGridlockRevert,
|
|
3353
|
-
openBuiltInWorkflow,
|
|
3354
|
-
openPickerRoute,
|
|
3355
3333
|
paneShortcutItems,
|
|
3356
|
-
persistLayoutChange,
|
|
3357
3334
|
pluginCommandItems,
|
|
3358
3335
|
pluginRegistry,
|
|
3359
3336
|
rootModeInfo.kind,
|
|
@@ -3787,31 +3764,7 @@ export function CommandBar({
|
|
|
3787
3764
|
if (currentRoute.kind === "mode") {
|
|
3788
3765
|
switch (currentRoute.screen) {
|
|
3789
3766
|
case "themes": {
|
|
3790
|
-
const
|
|
3791
|
-
const filtered = currentRoute.query
|
|
3792
|
-
? themeOptions.filter((theme) => (
|
|
3793
|
-
theme.name.toLowerCase().includes(currentRoute.query.toLowerCase())
|
|
3794
|
-
|| theme.id.includes(currentRoute.query.toLowerCase())
|
|
3795
|
-
))
|
|
3796
|
-
: themeOptions;
|
|
3797
|
-
const results = filtered.map((theme) => ({
|
|
3798
|
-
id: `theme:${theme.id}`,
|
|
3799
|
-
label: theme.name,
|
|
3800
|
-
detail: theme.description,
|
|
3801
|
-
category: "Themes",
|
|
3802
|
-
kind: "theme" as const,
|
|
3803
|
-
current: theme.id === currentRoute.themeBaseId,
|
|
3804
|
-
themeId: theme.id,
|
|
3805
|
-
action: () => {
|
|
3806
|
-
const nextConfig = {
|
|
3807
|
-
...state.config,
|
|
3808
|
-
theme: theme.id,
|
|
3809
|
-
};
|
|
3810
|
-
dispatch({ type: "SET_THEME", theme: theme.id });
|
|
3811
|
-
void saveConfig(nextConfig);
|
|
3812
|
-
closeAll({ revertThemePreview: false });
|
|
3813
|
-
},
|
|
3814
|
-
}));
|
|
3767
|
+
const results = buildThemeItems(currentRoute.query, currentRoute.themeBaseId).items;
|
|
3815
3768
|
return {
|
|
3816
3769
|
kind: "mode",
|
|
3817
3770
|
title: "Change Theme",
|
|
@@ -3828,39 +3781,7 @@ export function CommandBar({
|
|
|
3828
3781
|
};
|
|
3829
3782
|
}
|
|
3830
3783
|
case "plugins": {
|
|
3831
|
-
const
|
|
3832
|
-
const toggleable = [...pluginRegistry.allPlugins.values()].filter((plugin) => plugin.toggleable);
|
|
3833
|
-
const filtered = currentRoute.query
|
|
3834
|
-
? toggleable.filter((plugin) => (
|
|
3835
|
-
plugin.name.toLowerCase().includes(currentRoute.query.toLowerCase())
|
|
3836
|
-
|| plugin.id.includes(currentRoute.query.toLowerCase())
|
|
3837
|
-
))
|
|
3838
|
-
: toggleable;
|
|
3839
|
-
const results = filtered.map((plugin) => {
|
|
3840
|
-
const enabled = !disabledPlugins.includes(plugin.id);
|
|
3841
|
-
const toggleAction = () => {
|
|
3842
|
-
dispatch({ type: "TOGGLE_PLUGIN", pluginId: plugin.id });
|
|
3843
|
-
const nextDisabled = enabled
|
|
3844
|
-
? [...disabledPlugins, plugin.id]
|
|
3845
|
-
: disabledPlugins.filter((entry) => entry !== plugin.id);
|
|
3846
|
-
if (enabled) {
|
|
3847
|
-
for (const paneId of pluginRegistry.getPluginPaneIds(plugin.id)) {
|
|
3848
|
-
pluginRegistry.hideWidget(paneId);
|
|
3849
|
-
}
|
|
3850
|
-
}
|
|
3851
|
-
void saveConfig({ ...state.config, disabledPlugins: nextDisabled });
|
|
3852
|
-
};
|
|
3853
|
-
return {
|
|
3854
|
-
id: `plugin:${plugin.id}`,
|
|
3855
|
-
label: plugin.name,
|
|
3856
|
-
detail: plugin.description || "",
|
|
3857
|
-
category: "Plugins",
|
|
3858
|
-
kind: "plugin" as const,
|
|
3859
|
-
checked: enabled,
|
|
3860
|
-
pluginToggle: toggleAction,
|
|
3861
|
-
action: toggleAction,
|
|
3862
|
-
};
|
|
3863
|
-
});
|
|
3784
|
+
const results = buildPluginItems(currentRoute.query);
|
|
3864
3785
|
return {
|
|
3865
3786
|
kind: "mode",
|
|
3866
3787
|
title: "Manage Plugins",
|
|
@@ -3877,237 +3798,7 @@ export function CommandBar({
|
|
|
3877
3798
|
};
|
|
3878
3799
|
}
|
|
3879
3800
|
case "layout": {
|
|
3880
|
-
const
|
|
3881
|
-
const focusedPane = state.focusedPaneId ? findPaneInstance(currentLayout, state.focusedPaneId) : null;
|
|
3882
|
-
const focusedPaneDef = focusedPane ? pluginRegistry.panes.get(focusedPane.paneId) : null;
|
|
3883
|
-
const dockedPaneIds = getDockedPaneIds(currentLayout);
|
|
3884
|
-
const layoutHistory = state.layoutHistory[state.config.activeLayoutIndex];
|
|
3885
|
-
const layoutSnapshot = JSON.stringify(currentLayout);
|
|
3886
|
-
const canMove = (direction: "left" | "right" | "above" | "below") => (
|
|
3887
|
-
!!focusedPane && JSON.stringify(movePaneRelative(currentLayout, focusedPane.instanceId, direction)) !== layoutSnapshot
|
|
3888
|
-
);
|
|
3889
|
-
|
|
3890
|
-
const layoutItems: ResultItem[] = [];
|
|
3891
|
-
if (focusedPane && focusedPaneDef) {
|
|
3892
|
-
const focusedFloating = currentLayout.floating.find((entry) => entry.instanceId === focusedPane.instanceId);
|
|
3893
|
-
layoutItems.push({
|
|
3894
|
-
id: "layout-toggle-mode",
|
|
3895
|
-
label: focusedFloating ? "Dock Pane" : "Float Pane",
|
|
3896
|
-
detail: focusedFloating ? "Return the focused window to the layout" : "Detach the focused pane into a floating window",
|
|
3897
|
-
category: "Focused Pane",
|
|
3898
|
-
kind: "action",
|
|
3899
|
-
action: () => {
|
|
3900
|
-
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
3901
|
-
const nextLayout = focusedFloating
|
|
3902
|
-
? dockPane(currentLayout, focusedPane.instanceId)
|
|
3903
|
-
: floatPane(currentLayout, focusedPane.instanceId, width, height, focusedPaneDef);
|
|
3904
|
-
persistLayoutChange(nextLayout);
|
|
3905
|
-
closeAll({ revertThemePreview: false });
|
|
3906
|
-
},
|
|
3907
|
-
});
|
|
3908
|
-
|
|
3909
|
-
const moveActions: Array<{
|
|
3910
|
-
id: string;
|
|
3911
|
-
label: string;
|
|
3912
|
-
direction: "left" | "right" | "above" | "below";
|
|
3913
|
-
available: boolean;
|
|
3914
|
-
unavailableDetail: string;
|
|
3915
|
-
}> = [
|
|
3916
|
-
{ id: "layout-move-left", label: "Move Left", direction: "left", available: canMove("left"), unavailableDetail: "No column to the left" },
|
|
3917
|
-
{ id: "layout-move-right", label: "Move Right", direction: "right", available: canMove("right"), unavailableDetail: "No column to the right" },
|
|
3918
|
-
{ id: "layout-move-up", label: "Move Up", direction: "above", available: canMove("above"), unavailableDetail: "No pane above" },
|
|
3919
|
-
{ id: "layout-move-down", label: "Move Down", direction: "below", available: canMove("below"), unavailableDetail: "No pane below" },
|
|
3920
|
-
];
|
|
3921
|
-
|
|
3922
|
-
moveActions.forEach((entry) => {
|
|
3923
|
-
layoutItems.push({
|
|
3924
|
-
id: entry.id,
|
|
3925
|
-
label: entry.label,
|
|
3926
|
-
detail: entry.available ? "Reposition the focused pane" : entry.unavailableDetail,
|
|
3927
|
-
category: "Focused Pane",
|
|
3928
|
-
kind: entry.available ? "action" : "info",
|
|
3929
|
-
disabled: !entry.available,
|
|
3930
|
-
action: entry.available
|
|
3931
|
-
? () => {
|
|
3932
|
-
persistLayoutChange(movePaneRelative(currentLayout, focusedPane.instanceId, entry.direction));
|
|
3933
|
-
closeAll({ revertThemePreview: false });
|
|
3934
|
-
}
|
|
3935
|
-
: () => {},
|
|
3936
|
-
});
|
|
3937
|
-
});
|
|
3938
|
-
|
|
3939
|
-
layoutItems.push({
|
|
3940
|
-
id: "layout-swap",
|
|
3941
|
-
label: "Swap With…",
|
|
3942
|
-
detail: dockedPaneIds.length + currentLayout.floating.length > 1
|
|
3943
|
-
? "Choose another pane to swap positions"
|
|
3944
|
-
: "Need at least two panes",
|
|
3945
|
-
category: "Focused Pane",
|
|
3946
|
-
kind: "action",
|
|
3947
|
-
disabled: dockedPaneIds.length + currentLayout.floating.length <= 1,
|
|
3948
|
-
action: () => {
|
|
3949
|
-
const options = [
|
|
3950
|
-
...dockedPaneIds,
|
|
3951
|
-
...currentLayout.floating.map((entry) => entry.instanceId),
|
|
3952
|
-
]
|
|
3953
|
-
.filter((paneId) => paneId !== focusedPane.instanceId)
|
|
3954
|
-
.map((paneId) => {
|
|
3955
|
-
const instance = findPaneInstance(currentLayout, paneId)!;
|
|
3956
|
-
return {
|
|
3957
|
-
id: paneId,
|
|
3958
|
-
label: instance.title || pluginRegistry.panes.get(instance.paneId)?.name || instance.paneId,
|
|
3959
|
-
detail: currentLayout.floating.some((entry) => entry.instanceId === paneId) ? "Floating window" : "Docked pane",
|
|
3960
|
-
description: currentLayout.floating.some((entry) => entry.instanceId === paneId) ? "Floating window" : "Docked pane",
|
|
3961
|
-
};
|
|
3962
|
-
});
|
|
3963
|
-
if (options.length === 0) return;
|
|
3964
|
-
openPickerRoute({
|
|
3965
|
-
kind: "picker",
|
|
3966
|
-
pickerId: "layout-swap",
|
|
3967
|
-
title: "Swap With…",
|
|
3968
|
-
query: "",
|
|
3969
|
-
selectedIdx: 0,
|
|
3970
|
-
hoveredIdx: null,
|
|
3971
|
-
options,
|
|
3972
|
-
payload: { sourcePaneId: focusedPane.instanceId },
|
|
3973
|
-
});
|
|
3974
|
-
},
|
|
3975
|
-
});
|
|
3976
|
-
|
|
3977
|
-
layoutItems.push({
|
|
3978
|
-
id: "layout-duplicate",
|
|
3979
|
-
label: "Duplicate Pane",
|
|
3980
|
-
detail: "Create another instance next to the focused pane",
|
|
3981
|
-
category: "Focused Pane",
|
|
3982
|
-
kind: "action",
|
|
3983
|
-
action: () => {
|
|
3984
|
-
duplicatePane(focusedPane.instanceId);
|
|
3985
|
-
closeAll({ revertThemePreview: false });
|
|
3986
|
-
},
|
|
3987
|
-
});
|
|
3988
|
-
|
|
3989
|
-
layoutItems.push({
|
|
3990
|
-
id: "layout-close-pane",
|
|
3991
|
-
label: "Close Pane",
|
|
3992
|
-
detail: "Remove the focused pane from the layout",
|
|
3993
|
-
category: "Focused Pane",
|
|
3994
|
-
kind: "action",
|
|
3995
|
-
action: () => {
|
|
3996
|
-
persistLayoutChange(removePane(currentLayout, focusedPane.instanceId));
|
|
3997
|
-
closeAll({ revertThemePreview: false });
|
|
3998
|
-
},
|
|
3999
|
-
});
|
|
4000
|
-
} else {
|
|
4001
|
-
layoutItems.push({
|
|
4002
|
-
id: "layout-no-focused-pane",
|
|
4003
|
-
label: "No focused pane",
|
|
4004
|
-
detail: "Focus a pane to show pane-specific layout actions",
|
|
4005
|
-
category: "Focused Pane",
|
|
4006
|
-
kind: "info",
|
|
4007
|
-
action: () => {},
|
|
4008
|
-
});
|
|
4009
|
-
}
|
|
4010
|
-
|
|
4011
|
-
layoutItems.push({
|
|
4012
|
-
id: "layout-undo",
|
|
4013
|
-
label: "Undo Layout Change",
|
|
4014
|
-
detail: (layoutHistory?.past.length ?? 0) > 0 ? "Restore the previous layout state" : "No previous layout state",
|
|
4015
|
-
category: "Current Layout",
|
|
4016
|
-
kind: "action",
|
|
4017
|
-
disabled: (layoutHistory?.past.length ?? 0) === 0,
|
|
4018
|
-
action: () => {
|
|
4019
|
-
if ((layoutHistory?.past.length ?? 0) === 0) return;
|
|
4020
|
-
dispatch({ type: "UNDO_LAYOUT" });
|
|
4021
|
-
closeAll({ revertThemePreview: false });
|
|
4022
|
-
},
|
|
4023
|
-
});
|
|
4024
|
-
layoutItems.push({
|
|
4025
|
-
id: "layout-redo",
|
|
4026
|
-
label: "Redo Layout Change",
|
|
4027
|
-
detail: (layoutHistory?.future.length ?? 0) > 0 ? "Reapply the next layout state" : "No later layout state",
|
|
4028
|
-
category: "Current Layout",
|
|
4029
|
-
kind: "action",
|
|
4030
|
-
disabled: (layoutHistory?.future.length ?? 0) === 0,
|
|
4031
|
-
action: () => {
|
|
4032
|
-
if ((layoutHistory?.future.length ?? 0) === 0) return;
|
|
4033
|
-
dispatch({ type: "REDO_LAYOUT" });
|
|
4034
|
-
closeAll({ revertThemePreview: false });
|
|
4035
|
-
},
|
|
4036
|
-
});
|
|
4037
|
-
layoutItems.push({
|
|
4038
|
-
id: "layout-reset",
|
|
4039
|
-
label: "Reset Current Layout",
|
|
4040
|
-
detail: "Restore the default two-pane layout",
|
|
4041
|
-
category: "Current Layout",
|
|
4042
|
-
kind: "action",
|
|
4043
|
-
action: () => {
|
|
4044
|
-
persistLayoutChange(cloneLayout(DEFAULT_LAYOUT));
|
|
4045
|
-
closeAll({ revertThemePreview: false });
|
|
4046
|
-
},
|
|
4047
|
-
});
|
|
4048
|
-
layoutItems.push({
|
|
4049
|
-
id: "layout-gridlock",
|
|
4050
|
-
label: "Gridlock All Windows",
|
|
4051
|
-
detail: currentLayout.floating.length > 0
|
|
4052
|
-
? "Infer a tiled layout from the current window positions"
|
|
4053
|
-
: "Retile all panes from their current arrangement",
|
|
4054
|
-
category: "Current Layout",
|
|
4055
|
-
kind: "action",
|
|
4056
|
-
action: () => {
|
|
4057
|
-
const { width, height } = pluginRegistry.getTermSizeFn();
|
|
4058
|
-
persistLayoutChange(gridlockAllPanes(currentLayout, { x: 0, y: 0, width, height }));
|
|
4059
|
-
notifyGridlockRevert();
|
|
4060
|
-
closeAll({ revertThemePreview: false });
|
|
4061
|
-
},
|
|
4062
|
-
});
|
|
4063
|
-
layoutItems.push({
|
|
4064
|
-
id: "layout-rename",
|
|
4065
|
-
label: "Rename Layout",
|
|
4066
|
-
detail: "Change the current saved layout name",
|
|
4067
|
-
category: "Current Layout",
|
|
4068
|
-
kind: "action",
|
|
4069
|
-
action: () => openBuiltInWorkflow("rename-layout"),
|
|
4070
|
-
});
|
|
4071
|
-
layoutItems.push({
|
|
4072
|
-
id: "layout-duplicate-layout",
|
|
4073
|
-
label: "Duplicate Layout",
|
|
4074
|
-
detail: "Create a copy of the current layout",
|
|
4075
|
-
category: "Current Layout",
|
|
4076
|
-
kind: "action",
|
|
4077
|
-
action: () => {
|
|
4078
|
-
dispatch({ type: "DUPLICATE_LAYOUT", index: state.config.activeLayoutIndex });
|
|
4079
|
-
closeAll({ revertThemePreview: false });
|
|
4080
|
-
},
|
|
4081
|
-
});
|
|
4082
|
-
layoutItems.push({
|
|
4083
|
-
id: "layout-new",
|
|
4084
|
-
label: "New Layout",
|
|
4085
|
-
detail: "Create a fresh saved layout",
|
|
4086
|
-
category: "Current Layout",
|
|
4087
|
-
kind: "action",
|
|
4088
|
-
action: () => openBuiltInWorkflow("new-layout"),
|
|
4089
|
-
});
|
|
4090
|
-
|
|
4091
|
-
state.config.layouts.forEach((savedLayout, index) => {
|
|
4092
|
-
layoutItems.push({
|
|
4093
|
-
id: `layout-switch:${index}`,
|
|
4094
|
-
label: savedLayout.name,
|
|
4095
|
-
detail: index === state.config.activeLayoutIndex ? "Current layout" : "Switch to this saved layout",
|
|
4096
|
-
right: getLayoutPreview(savedLayout.layout),
|
|
4097
|
-
category: "Saved Layouts",
|
|
4098
|
-
kind: "action",
|
|
4099
|
-
current: index === state.config.activeLayoutIndex,
|
|
4100
|
-
action: () => {
|
|
4101
|
-
dispatch({ type: "SWITCH_LAYOUT", index });
|
|
4102
|
-
closeAll({ revertThemePreview: false });
|
|
4103
|
-
},
|
|
4104
|
-
});
|
|
4105
|
-
});
|
|
4106
|
-
|
|
4107
|
-
const results = currentRoute.query
|
|
4108
|
-
? fuzzyFilter(layoutItems, currentRoute.query, (item) => `${item.label} ${item.detail} ${item.right || ""}`)
|
|
4109
|
-
: layoutItems;
|
|
4110
|
-
|
|
3801
|
+
const results = buildLayoutItems(currentRoute.query);
|
|
4111
3802
|
return {
|
|
4112
3803
|
kind: "mode",
|
|
4113
3804
|
title: "Layout Actions",
|
|
@@ -4218,6 +3909,9 @@ export function CommandBar({
|
|
|
4218
3909
|
activeMatch,
|
|
4219
3910
|
activeTickerData,
|
|
4220
3911
|
activeTickerSymbol,
|
|
3912
|
+
buildLayoutItems,
|
|
3913
|
+
buildPluginItems,
|
|
3914
|
+
buildThemeItems,
|
|
4221
3915
|
closeAll,
|
|
4222
3916
|
createPaneTemplateItem,
|
|
4223
3917
|
currentRoute,
|
|
@@ -5574,33 +5268,14 @@ export function CommandBar({
|
|
|
5574
5268
|
const renderConfirmBody = () => {
|
|
5575
5269
|
if (currentRoute?.kind !== "confirm") return null;
|
|
5576
5270
|
return (
|
|
5577
|
-
<
|
|
5578
|
-
{currentRoute
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
<Box height={1}>
|
|
5586
|
-
<Text fg={colors.negative}>{truncateText(currentRoute.error, queryDisplayWidth)}</Text>
|
|
5587
|
-
</Box>
|
|
5588
|
-
)}
|
|
5589
|
-
{currentRoute.pending && (
|
|
5590
|
-
<Box height={1}>
|
|
5591
|
-
<Spinner label="Working…" />
|
|
5592
|
-
</Box>
|
|
5593
|
-
)}
|
|
5594
|
-
<Box flexGrow={1} />
|
|
5595
|
-
<Box flexDirection="row" gap={1}>
|
|
5596
|
-
<Button
|
|
5597
|
-
label={currentRoute.confirmLabel}
|
|
5598
|
-
variant={currentRoute.tone === "danger" ? "danger" : "primary"}
|
|
5599
|
-
onPress={() => { void confirmCurrentRoute(); }}
|
|
5600
|
-
disabled={currentRoute.pending}
|
|
5601
|
-
/>
|
|
5602
|
-
</Box>
|
|
5603
|
-
</Box>
|
|
5271
|
+
<CommandBarConfirmBody
|
|
5272
|
+
route={currentRoute}
|
|
5273
|
+
bodyHeight={bodyHeight}
|
|
5274
|
+
contentPadding={contentPadding}
|
|
5275
|
+
paletteText={paletteText}
|
|
5276
|
+
queryDisplayWidth={queryDisplayWidth}
|
|
5277
|
+
onConfirm={() => { void confirmCurrentRoute(); }}
|
|
5278
|
+
/>
|
|
5604
5279
|
);
|
|
5605
5280
|
};
|
|
5606
5281
|
|