Make the command center stable and workspace-aware (#2749)
* feat(app): expand command center actions Keep command center navigation stable by revealing only clipped rows without smooth centering. Prioritize a concise default list while making workspace, Git, pane, and creation actions searchable through the existing contribution and action-policy paths. * test(app): search for query-only command action Add project is intentionally absent from the unfiltered command center. Exercise the overlay flow through search before selecting it.
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import type { GitAction, GitActions } from "@/git/policy";
|
||||
import type { KeyboardActionDefinition } from "@/keyboard/keyboard-action-dispatcher";
|
||||
import {
|
||||
buildWorkspaceCommandCenterContributions,
|
||||
type WorkspaceCommandCenterSource,
|
||||
} from "./workspace-contributions";
|
||||
|
||||
function gitAction(id: GitAction["id"], label: string): GitAction {
|
||||
return {
|
||||
id,
|
||||
label,
|
||||
pendingLabel: `${label} pending`,
|
||||
successLabel: `${label} complete`,
|
||||
disabled: false,
|
||||
status: "idle",
|
||||
startsGroup: false,
|
||||
handler: () => undefined,
|
||||
};
|
||||
}
|
||||
|
||||
function source(gitActions: GitActions): {
|
||||
value: WorkspaceCommandCenterSource;
|
||||
runGitActions: GitAction[];
|
||||
dispatched: KeyboardActionDefinition[];
|
||||
} {
|
||||
const runGitActions: GitAction[] = [];
|
||||
const dispatched: KeyboardActionDefinition[] = [];
|
||||
return {
|
||||
value: {
|
||||
gitActions,
|
||||
labels: {
|
||||
section: "Workspace actions",
|
||||
newAgent: "New agent",
|
||||
newTerminal: "New terminal",
|
||||
newBrowser: "New browser",
|
||||
splitRight: "Split pane right",
|
||||
splitDown: "Split pane down",
|
||||
},
|
||||
icons: {},
|
||||
shortcuts: {},
|
||||
capabilities: { canSplitPanes: true, canOpenBrowserTabs: true },
|
||||
dispatch: (action) => dispatched.push(action),
|
||||
runGitAction: (action) => runGitActions.push(action),
|
||||
},
|
||||
runGitActions,
|
||||
dispatched,
|
||||
};
|
||||
}
|
||||
|
||||
describe("workspace command center contributions", () => {
|
||||
it("makes only the policy-selected primary Git action default-visible and runs it", () => {
|
||||
const primary = gitAction("commit", "Commit");
|
||||
const fixture = source({
|
||||
primary,
|
||||
secondary: [gitAction("push", "Push")],
|
||||
menu: [],
|
||||
});
|
||||
|
||||
const contributions = buildWorkspaceCommandCenterContributions(fixture.value);
|
||||
const gitContributions = contributions.filter((item) => item.id.startsWith("git:"));
|
||||
const defaultGitContributions = gitContributions.filter((item) => item.visibility === "always");
|
||||
|
||||
expect(defaultGitContributions.map((item) => item.id)).toEqual(["git:commit"]);
|
||||
defaultGitContributions[0].run();
|
||||
expect(fixture.runGitActions).toEqual([primary]);
|
||||
});
|
||||
|
||||
it("does not duplicate a primary action retained in the secondary policy list", () => {
|
||||
const primary = gitAction("pull", "Pull");
|
||||
const fixture = source({
|
||||
primary,
|
||||
secondary: [primary, gitAction("push", "Push")],
|
||||
menu: [],
|
||||
});
|
||||
|
||||
const contributions = buildWorkspaceCommandCenterContributions(fixture.value);
|
||||
|
||||
expect(contributions.filter((item) => item.id === "git:pull")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("orders New agent before Git and keeps terminal, browser, and splits search-only", () => {
|
||||
const fixture = source({
|
||||
primary: gitAction("commit", "Commit"),
|
||||
secondary: [],
|
||||
menu: [],
|
||||
});
|
||||
|
||||
const contributions = buildWorkspaceCommandCenterContributions(fixture.value);
|
||||
|
||||
expect(contributions.map(({ id, rank, visibility }) => ({ id, rank, visibility }))).toEqual([
|
||||
{ id: "tab:new-agent", rank: 0, visibility: "always" },
|
||||
{ id: "git:commit", rank: 1, visibility: "always" },
|
||||
{ id: "tab:new-terminal", rank: 2, visibility: "query" },
|
||||
{ id: "tab:new-browser", rank: 3, visibility: "query" },
|
||||
{ id: "pane:split-right", rank: 4, visibility: "query" },
|
||||
{ id: "pane:split-down", rank: 5, visibility: "query" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("omits browser and split actions when their existing capabilities are unavailable", () => {
|
||||
const fixture = source({ primary: null, secondary: [], menu: [] });
|
||||
fixture.value.capabilities = { canSplitPanes: false, canOpenBrowserTabs: false };
|
||||
|
||||
const contributions = buildWorkspaceCommandCenterContributions(fixture.value);
|
||||
|
||||
expect(contributions.map((item) => item.id)).toEqual(["tab:new-agent", "tab:new-terminal"]);
|
||||
});
|
||||
|
||||
it("dispatches every tab and pane command to the workspace scope", () => {
|
||||
const fixture = source({ primary: null, secondary: [], menu: [] });
|
||||
const contributions = buildWorkspaceCommandCenterContributions(fixture.value);
|
||||
|
||||
for (const contribution of contributions) contribution.run();
|
||||
|
||||
expect(fixture.dispatched).toEqual([
|
||||
{ id: "workspace.tab.new", scope: "workspace" },
|
||||
{ id: "workspace.terminal.new", scope: "workspace" },
|
||||
{ id: "workspace.browser.new", scope: "workspace" },
|
||||
{ id: "workspace.pane.split.right", scope: "workspace" },
|
||||
{ id: "workspace.pane.split.down", scope: "workspace" },
|
||||
]);
|
||||
});
|
||||
|
||||
it("keeps workspace creation commands available outside Git", () => {
|
||||
const fixture = source({ primary: null, secondary: [], menu: [] });
|
||||
|
||||
const contributions = buildWorkspaceCommandCenterContributions(fixture.value);
|
||||
|
||||
expect(contributions.map((item) => item.id)).toEqual([
|
||||
"tab:new-agent",
|
||||
"tab:new-terminal",
|
||||
"tab:new-browser",
|
||||
"pane:split-right",
|
||||
"pane:split-down",
|
||||
]);
|
||||
expect(contributions.some((item) => item.id.startsWith("git:"))).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user