diff --git a/src/extension/view-dist/index.html b/src/extension/view-dist/index.html
index 7f0f7adc..23bb8a6a 100644
--- a/src/extension/view-dist/index.html
+++ b/src/extension/view-dist/index.html
@@ -5,7 +5,7 @@
Vite + React + TS
-
+
diff --git a/src/internal/adapters.ts b/src/internal/adapters.ts
index 42f9b51d..b2d37701 100644
--- a/src/internal/adapters.ts
+++ b/src/internal/adapters.ts
@@ -1,6 +1,8 @@
import * as vscode from "vscode";
import { CONFIG_SECTION } from "../pkg/config-constants";
import { ButtonConfig, RefreshButtonConfig } from "../pkg/types";
+import { ensureIdsInArray, ButtonConfigWithOptionalId } from "./utils/ensure-id";
+
const DEFAULT_REFRESH_CONFIG: RefreshButtonConfig = {
color: "#00BCD4",
enabled: true,
@@ -28,7 +30,7 @@ export type StatusBarCreator = (
export type QuickPickCreator = () => vscode.QuickPick;
-const getButtonsFromConfig = (config: vscode.WorkspaceConfiguration): ButtonConfig[] =>
+const getButtonsFromConfig = (config: vscode.WorkspaceConfiguration): ButtonConfigWithOptionalId[] =>
config.get("buttons") || [];
const getRefreshConfigFromConfig = (config: vscode.WorkspaceConfiguration): RefreshButtonConfig =>
@@ -40,7 +42,8 @@ const isQuickCommandButtonsConfigChange = (event: vscode.ConfigurationChangeEven
export const createVSCodeConfigReader = (): ConfigReader => ({
getButtons: () => {
const config = vscode.workspace.getConfiguration(CONFIG_SECTION);
- return getButtonsFromConfig(config);
+ const buttons = getButtonsFromConfig(config);
+ return ensureIdsInArray(buttons);
},
getRefreshConfig: () => {
const config = vscode.workspace.getConfiguration(CONFIG_SECTION);
diff --git a/src/internal/utils/ensure-id.ts b/src/internal/utils/ensure-id.ts
new file mode 100644
index 00000000..438a736e
--- /dev/null
+++ b/src/internal/utils/ensure-id.ts
@@ -0,0 +1,20 @@
+import { randomUUID } from "crypto";
+import { ButtonConfig } from "../../pkg/types";
+
+export type ButtonConfigWithOptionalId = Omit & {
+ group?: ButtonConfigWithOptionalId[];
+ id?: string;
+};
+
+export const ensureId = (config: ButtonConfigWithOptionalId): ButtonConfig => {
+ const { group, id, ...restConfig } = config;
+
+ return {
+ ...restConfig,
+ id: id ?? randomUUID(),
+ ...(group !== undefined && { group: group.map(ensureId) }),
+ };
+};
+
+export const ensureIdsInArray = (configs: ButtonConfigWithOptionalId[]): ButtonConfig[] =>
+ configs.map((config) => ensureId(config));
diff --git a/src/shared/types.ts b/src/shared/types.ts
index 0002c655..1fd83b37 100644
--- a/src/shared/types.ts
+++ b/src/shared/types.ts
@@ -3,6 +3,7 @@ export type ButtonConfig = {
command?: string;
executeAll?: boolean;
group?: ButtonConfig[];
+ id: string;
name: string;
shortcut?: string;
terminalName?: string;
diff --git a/src/tests/adapters.spec.ts b/src/tests/adapters.spec.ts
index 606a62ec..fe6606c9 100644
--- a/src/tests/adapters.spec.ts
+++ b/src/tests/adapters.spec.ts
@@ -7,7 +7,7 @@ describe("adapters", () => {
});
describe("getButtonsFromConfig", () => {
- it("should return buttons from config", () => {
+ it("should return buttons from config with IDs added", () => {
const mockButtons = [
{ command: "test command", name: "Test Button" },
{ command: "another command", name: "Another Button" },
@@ -22,11 +22,63 @@ describe("adapters", () => {
const configReader = createVSCodeConfigReader();
const result = configReader.getButtons();
- expect(result).toEqual(mockButtons);
+ expect(result).toHaveLength(2);
+ expect(result[0].name).toBe("Test Button");
+ expect(result[1].name).toBe("Another Button");
+ expect(result[0].id).toBeDefined();
+ expect(result[1].id).toBeDefined();
+ expect(result[0].id).not.toBe(result[1].id);
expect(vscode.workspace.getConfiguration).toHaveBeenCalledWith("quickCommandButtons");
expect(mockConfig.get).toHaveBeenCalledWith("buttons");
});
+ it("should preserve existing IDs in buttons", () => {
+ const existingId1 = "existing-id-1";
+ const existingId2 = "existing-id-2";
+ const mockButtons = [
+ { command: "test command", name: "Test Button", id: existingId1 },
+ { command: "another command", name: "Another Button", id: existingId2 },
+ ];
+
+ const mockConfig = {
+ get: jest.fn((key: string) => (key === "buttons" ? mockButtons : undefined)),
+ };
+
+ (vscode.workspace.getConfiguration as jest.Mock).mockReturnValue(mockConfig);
+
+ const configReader = createVSCodeConfigReader();
+ const result = configReader.getButtons();
+
+ expect(result[0].id).toBe(existingId1);
+ expect(result[1].id).toBe(existingId2);
+ });
+
+ it("should add IDs to nested group items", () => {
+ const mockButtons = [
+ {
+ name: "Parent Group",
+ group: [
+ { command: "child 1", name: "Child 1" },
+ { command: "child 2", name: "Child 2" },
+ ],
+ },
+ ];
+
+ const mockConfig = {
+ get: jest.fn((key: string) => (key === "buttons" ? mockButtons : undefined)),
+ };
+
+ (vscode.workspace.getConfiguration as jest.Mock).mockReturnValue(mockConfig);
+
+ const configReader = createVSCodeConfigReader();
+ const result = configReader.getButtons();
+
+ expect(result[0].id).toBeDefined();
+ expect(result[0].group![0].id).toBeDefined();
+ expect(result[0].group![1].id).toBeDefined();
+ expect(result[0].group![0].id).not.toBe(result[0].group![1].id);
+ });
+
it("should return empty array when no buttons in config", () => {
const mockConfig = {
get: jest.fn(() => undefined),
diff --git a/src/tests/command-executor.spec.ts b/src/tests/command-executor.spec.ts
index 2df1cf6b..94d3b0dd 100644
--- a/src/tests/command-executor.spec.ts
+++ b/src/tests/command-executor.spec.ts
@@ -13,12 +13,12 @@ describe("command-executor", () => {
it("should return empty array for unique shortcuts", () => {
const items = [
{
- command: { name: "test1", shortcut: "a" } as ButtonConfig,
+ command: { id: "test-1", name: "test1", shortcut: "a" } as ButtonConfig,
description: "",
label: "Test 1",
},
{
- command: { name: "test2", shortcut: "b" } as ButtonConfig,
+ command: { id: "test-2", name: "test2", shortcut: "b" } as ButtonConfig,
description: "",
label: "Test 2",
},
@@ -32,17 +32,17 @@ describe("command-executor", () => {
it("should return duplicated shortcuts (case insensitive)", () => {
const items = [
{
- command: { name: "test1", shortcut: "a" } as ButtonConfig,
+ command: { id: "test-1", name: "test1", shortcut: "a" } as ButtonConfig,
description: "",
label: "Test 1",
},
{
- command: { name: "test2", shortcut: "A" } as ButtonConfig,
+ command: { id: "test-2", name: "test2", shortcut: "A" } as ButtonConfig,
description: "",
label: "Test 2",
},
{
- command: { name: "test3", shortcut: "b" } as ButtonConfig,
+ command: { id: "test-3", name: "test3", shortcut: "b" } as ButtonConfig,
description: "",
label: "Test 3",
},
@@ -56,22 +56,22 @@ describe("command-executor", () => {
it("should return multiple duplicated shortcuts", () => {
const items = [
{
- command: { name: "test1", shortcut: "a" } as ButtonConfig,
+ command: { id: "test-1", name: "test1", shortcut: "a" } as ButtonConfig,
description: "",
label: "Test 1",
},
{
- command: { name: "test2", shortcut: "A" } as ButtonConfig,
+ command: { id: "test-2", name: "test2", shortcut: "A" } as ButtonConfig,
description: "",
label: "Test 2",
},
{
- command: { name: "test3", shortcut: "b" } as ButtonConfig,
+ command: { id: "test-3", name: "test3", shortcut: "b" } as ButtonConfig,
description: "",
label: "Test 3",
},
{
- command: { name: "test4", shortcut: "B" } as ButtonConfig,
+ command: { id: "test-4", name: "test4", shortcut: "B" } as ButtonConfig,
description: "",
label: "Test 4",
},
@@ -85,12 +85,12 @@ describe("command-executor", () => {
it("should handle items without shortcuts", () => {
const items = [
{
- command: { name: "test1" } as ButtonConfig,
+ command: { id: "test-1", name: "test1" } as ButtonConfig,
description: "",
label: "Test 1",
},
{
- command: { name: "test2", shortcut: "a" } as ButtonConfig,
+ command: { id: "test-2", name: "test2", shortcut: "a" } as ButtonConfig,
description: "",
label: "Test 2",
},
@@ -116,12 +116,12 @@ describe("command-executor", () => {
it("should handle all items without shortcuts", () => {
const items = [
{
- command: { name: "test1" } as ButtonConfig,
+ command: { id: "test-1", name: "test1" } as ButtonConfig,
description: "",
label: "Test 1",
},
{
- command: { name: "test2" } as ButtonConfig,
+ command: { id: "test-2", name: "test2" } as ButtonConfig,
description: "",
label: "Test 2",
},
@@ -136,17 +136,17 @@ describe("command-executor", () => {
describe("findShortcutItem", () => {
const items = [
{
- command: { name: "test1", shortcut: "a" } as ButtonConfig,
+ command: { id: "test-1", name: "test1", shortcut: "a" } as ButtonConfig,
description: "",
label: "Test 1",
},
{
- command: { name: "test2", shortcut: "B" } as ButtonConfig,
+ command: { id: "test-2", name: "test2", shortcut: "B" } as ButtonConfig,
description: "",
label: "Test 2",
},
{
- command: { name: "test3" } as ButtonConfig,
+ command: { id: "test-3", name: "test3" } as ButtonConfig,
description: "",
label: "Test 3",
},
@@ -191,7 +191,7 @@ describe("command-executor", () => {
it("should return undefined when no items have shortcuts", () => {
const itemsWithoutShortcuts = [
{
- command: { name: "test1" } as ButtonConfig,
+ command: { id: "test-1", name: "test1" } as ButtonConfig,
description: "",
label: "Test 1",
},
@@ -229,7 +229,7 @@ describe("command-executor", () => {
it("should handle Arabic characters", () => {
const arabicItems = [
{
- command: { name: "test1", shortcut: "z" } as ButtonConfig,
+ command: { id: "test-arabic", name: "test1", shortcut: "z" } as ButtonConfig,
description: "",
label: "Test 1",
},
@@ -242,7 +242,7 @@ describe("command-executor", () => {
it("should handle Hebrew characters", () => {
const hebrewItems = [
{
- command: { name: "test1", shortcut: "e" } as ButtonConfig,
+ command: { id: "test-hebrew", name: "test1", shortcut: "e" } as ButtonConfig,
description: "",
label: "Test 1",
},
@@ -257,7 +257,8 @@ describe("command-executor", () => {
it("should return 'executeAll' for button with group and executeAll flag", () => {
const button: ButtonConfig = {
executeAll: true,
- group: [{ command: "echo test", name: "child" }],
+ group: [{ id: "child-1", command: "echo test", name: "child" }],
+ id: "test-group",
name: "test",
};
@@ -268,7 +269,8 @@ describe("command-executor", () => {
it("should return 'showQuickPick' for button with group but no executeAll flag", () => {
const button: ButtonConfig = {
- group: [{ command: "echo test", name: "child" }],
+ group: [{ id: "child-1", command: "echo test", name: "child" }],
+ id: "test-group",
name: "test",
};
@@ -280,7 +282,8 @@ describe("command-executor", () => {
it("should return 'showQuickPick' for button with group and executeAll set to false", () => {
const button: ButtonConfig = {
executeAll: false,
- group: [{ command: "echo test", name: "child" }],
+ group: [{ id: "child-1", command: "echo test", name: "child" }],
+ id: "test-group",
name: "test",
};
@@ -292,6 +295,7 @@ describe("command-executor", () => {
it("should return 'executeCommand' for button with command but no group", () => {
const button: ButtonConfig = {
command: "echo test",
+ id: "test-command",
name: "test",
};
@@ -302,6 +306,7 @@ describe("command-executor", () => {
it("should return 'invalid' for button without command and without group", () => {
const button: ButtonConfig = {
+ id: "test-invalid",
name: "test",
};
@@ -313,6 +318,7 @@ describe("command-executor", () => {
it("should return 'invalid' for button with empty command string", () => {
const button: ButtonConfig = {
command: "",
+ id: "test-empty",
name: "test",
};
@@ -325,7 +331,8 @@ describe("command-executor", () => {
const button: ButtonConfig = {
command: "echo test",
executeAll: false,
- group: [{ command: "echo child", name: "child" }],
+ group: [{ id: "child-1", command: "echo child", name: "child" }],
+ id: "test-mixed",
name: "test",
};
@@ -338,7 +345,8 @@ describe("command-executor", () => {
const button: ButtonConfig = {
command: "echo test",
executeAll: true,
- group: [{ command: "echo child", name: "child" }],
+ group: [{ id: "child-1", command: "echo child", name: "child" }],
+ id: "test-executeall",
name: "test",
};
@@ -353,6 +361,7 @@ describe("command-executor", () => {
const commands: ButtonConfig[] = [
{
command: "echo test",
+ id: "test-shortcut",
name: "Test Command",
shortcut: "t",
},
@@ -373,6 +382,7 @@ describe("command-executor", () => {
const commands: ButtonConfig[] = [
{
command: "echo test",
+ id: "test-no-shortcut",
name: "Test Command",
},
];
@@ -392,6 +402,7 @@ describe("command-executor", () => {
const commands: ButtonConfig[] = [
{
command: "",
+ id: "test-empty-cmd",
name: "Test Command",
shortcut: "t",
},
@@ -411,6 +422,7 @@ describe("command-executor", () => {
it("should handle command without command property", () => {
const commands: ButtonConfig[] = [
{
+ id: "test-no-cmd-prop",
name: "Test Command",
shortcut: "t",
},
@@ -431,14 +443,17 @@ describe("command-executor", () => {
const commands: ButtonConfig[] = [
{
command: "echo 1",
+ id: "cmd-1",
name: "Command 1",
shortcut: "1",
},
{
command: "echo 2",
+ id: "cmd-2",
name: "Command 2",
},
{
+ id: "cmd-3",
name: "Command 3",
shortcut: "3",
},
@@ -478,6 +493,7 @@ describe("command-executor", () => {
{
additionalProperty: "custom",
command: "echo test",
+ id: "test-preserve",
name: "Test Command",
} as ButtonConfig & { additionalProperty: string },
];
@@ -493,6 +509,7 @@ describe("command-executor", () => {
const mockTerminalExecutor = jest.fn();
const button: ButtonConfig = {
command: "echo test",
+ id: "test-terminal-1",
name: "Test Button",
};
@@ -503,7 +520,7 @@ describe("command-executor", () => {
false,
undefined,
"Test Button",
- expect.objectContaining({ command: "echo test", name: "Test Button" })
+ expect.objectContaining({ command: "echo test", id: "test-terminal-1", name: "Test Button" })
);
});
@@ -511,6 +528,7 @@ describe("command-executor", () => {
const mockTerminalExecutor = jest.fn();
const button: ButtonConfig = {
command: "echo test",
+ id: "test-vscode-api",
name: "Test Button",
useVsCodeApi: true,
};
@@ -522,7 +540,7 @@ describe("command-executor", () => {
true,
undefined,
"Test Button",
- expect.objectContaining({ command: "echo test", name: "Test Button", useVsCodeApi: true })
+ expect.objectContaining({ command: "echo test", id: "test-vscode-api", name: "Test Button", useVsCodeApi: true })
);
});
@@ -530,6 +548,7 @@ describe("command-executor", () => {
const mockTerminalExecutor = jest.fn();
const button: ButtonConfig = {
command: "echo test",
+ id: "test-terminal-name",
name: "Test Button",
terminalName: "Custom Terminal",
};
@@ -543,6 +562,7 @@ describe("command-executor", () => {
"Test Button",
expect.objectContaining({
command: "echo test",
+ id: "test-terminal-name",
name: "Test Button",
terminalName: "Custom Terminal",
})
@@ -553,6 +573,7 @@ describe("command-executor", () => {
const mockTerminalExecutor = jest.fn();
const button: ButtonConfig = {
command: "echo test",
+ id: "test-all-params",
name: "Test Button",
terminalName: "Custom Terminal",
useVsCodeApi: true,
@@ -567,6 +588,7 @@ describe("command-executor", () => {
"Test Button",
expect.objectContaining({
command: "echo test",
+ id: "test-all-params",
name: "Test Button",
terminalName: "Custom Terminal",
useVsCodeApi: true,
@@ -577,6 +599,7 @@ describe("command-executor", () => {
it("should not call terminalExecutor when command is undefined", () => {
const mockTerminalExecutor = jest.fn();
const button: ButtonConfig = {
+ id: "test-no-command",
name: "Test Button",
};
@@ -589,6 +612,7 @@ describe("command-executor", () => {
const mockTerminalExecutor = jest.fn();
const button: ButtonConfig = {
command: "",
+ id: "test-empty-command",
name: "Test Button",
};
@@ -604,15 +628,18 @@ describe("command-executor", () => {
const commands: ButtonConfig[] = [
{
command: "echo test1",
+ id: "rec-cmd-1",
name: "Command 1",
},
{
command: "echo test2",
+ id: "rec-cmd-2",
name: "Command 2",
useVsCodeApi: true,
},
{
command: "echo test3",
+ id: "rec-cmd-3",
name: "Command 3",
terminalName: "Custom Terminal",
},
@@ -652,14 +679,17 @@ describe("command-executor", () => {
group: [
{
command: "echo child1",
+ id: "child-1",
name: "Child 1",
},
{
command: "echo child2",
+ id: "child-2",
name: "Child 2",
useVsCodeApi: true,
},
],
+ id: "group-cmd",
name: "Group Command",
},
];
@@ -691,9 +721,11 @@ describe("command-executor", () => {
group: [
{
command: "echo child1",
+ id: "child-1",
name: "Child 1",
},
],
+ id: "group-no-exec",
name: "Group Command",
},
];
@@ -714,16 +746,20 @@ describe("command-executor", () => {
group: [
{
command: "echo level3",
+ id: "level-3",
name: "Level 3 Command",
},
],
+ id: "level-2-group",
name: "Level 2 Group",
},
{
command: "echo level2",
+ id: "level-2-cmd",
name: "Level 2 Command",
},
],
+ id: "level-1-group",
name: "Level 1 Group",
},
];
@@ -752,9 +788,11 @@ describe("command-executor", () => {
const commands: ButtonConfig[] = [
{
command: "echo valid",
+ id: "valid-cmd",
name: "Valid Command",
},
{
+ id: "invalid-cmd",
name: "Invalid Command",
},
];
@@ -775,10 +813,12 @@ describe("command-executor", () => {
const commands: ButtonConfig[] = [
{
command: "echo valid",
+ id: "valid-cmd",
name: "Valid Command",
},
{
command: "",
+ id: "empty-cmd",
name: "Empty Command",
},
];
@@ -808,6 +848,7 @@ describe("command-executor", () => {
const commands: ButtonConfig[] = [
{
command: "echo regular",
+ id: "regular-cmd",
name: "Regular Command",
},
{
@@ -815,9 +856,11 @@ describe("command-executor", () => {
group: [
{
command: "echo child",
+ id: "child-cmd",
name: "Child Command",
},
],
+ id: "group-with-exec",
name: "Group with executeAll",
},
{
@@ -825,12 +868,15 @@ describe("command-executor", () => {
group: [
{
command: "echo ignored",
+ id: "ignored-child",
name: "Ignored Child",
},
],
+ id: "group-without-exec",
name: "Group without executeAll",
},
{
+ id: "invalid-cmd",
name: "Invalid Command",
},
];
@@ -865,13 +911,16 @@ describe("command-executor", () => {
group: [
{
command: "echo leaf1",
+ id: "leaf-1",
name: "Leaf 1",
},
{
command: "echo leaf2",
+ id: "leaf-2",
name: "Leaf 2",
},
],
+ id: "branch-1",
name: "Branch 1",
},
{
@@ -879,16 +928,20 @@ describe("command-executor", () => {
group: [
{
command: "echo ignored",
+ id: "ignored-leaf",
name: "Ignored Leaf",
},
],
+ id: "branch-2",
name: "Branch 2",
},
{
command: "echo direct",
+ id: "direct-cmd",
name: "Direct Command",
},
],
+ id: "root-group",
name: "Root Group",
},
];
diff --git a/src/tests/command-tree-provider.spec.ts b/src/tests/command-tree-provider.spec.ts
index bf3c28f3..b0b6da8d 100644
--- a/src/tests/command-tree-provider.spec.ts
+++ b/src/tests/command-tree-provider.spec.ts
@@ -6,8 +6,8 @@ describe("command-tree-provider", () => {
describe("createTreeItems", () => {
it("should create command tree items from simple commands", () => {
const commands: ButtonConfig[] = [
- { command: "echo hello", name: "Test Command 1" },
- { command: "ls -la", name: "Test Command 2" },
+ { id: "test-cmd-1", command: "echo hello", name: "Test Command 1" },
+ { id: "test-cmd-2", command: "ls -la", name: "Test Command 2" },
];
const result = createTreeItems(commands);
@@ -29,6 +29,7 @@ describe("command-tree-provider", () => {
it("should create command tree items with VS Code API and terminal name", () => {
const commands: ButtonConfig[] = [
{
+ id: "vscode-cmd",
command: "workbench.action.openSettings",
name: "VS Code Command",
terminalName: "settings-terminal",
@@ -47,8 +48,8 @@ describe("command-tree-provider", () => {
it("should handle commands with empty command string", () => {
const commands: ButtonConfig[] = [
- { command: "", name: "Empty Command" },
- { name: "No Command Property" } as ButtonConfig,
+ { id: "empty-cmd", command: "", name: "Empty Command" },
+ { id: "no-cmd-prop", name: "No Command Property" } as ButtonConfig,
];
const result = createTreeItems(commands);
@@ -70,6 +71,7 @@ describe("command-tree-provider", () => {
it("should preserve original command objects without modification", () => {
const commands: ButtonConfig[] = [
{
+ id: "original-cmd",
color: "red",
command: "test command",
name: "Original Command",
@@ -86,14 +88,16 @@ describe("command-tree-provider", () => {
it("should handle mixed command configurations", () => {
const commands: ButtonConfig[] = [
- { command: "echo simple", name: "Simple" },
+ { id: "simple-cmd", command: "echo simple", name: "Simple" },
{
+ id: "complex-cmd",
command: "npm test",
name: "Complex",
terminalName: "test-terminal",
useVsCodeApi: false,
},
{
+ id: "vscode-reload",
command: "workbench.action.reload",
name: "VS Code",
useVsCodeApi: true,
@@ -113,11 +117,12 @@ describe("command-tree-provider", () => {
it("should handle nested groups", () => {
const commands: ButtonConfig[] = [
- { command: "echo simple", name: "Simple Command" },
+ { id: "simple-cmd", command: "echo simple", name: "Simple Command" },
{
+ id: "nested-group",
group: [
- { command: "echo sub1", name: "Sub Command 1" },
- { command: "echo sub2", name: "Sub Command 2" },
+ { id: "sub-cmd-1", command: "echo sub1", name: "Sub Command 1" },
+ { id: "sub-cmd-2", command: "echo sub2", name: "Sub Command 2" },
],
name: "Nested Group",
},
@@ -138,15 +143,17 @@ describe("command-tree-provider", () => {
describe("createTreeItems (root)", () => {
it("should create mixed command and group tree items", () => {
const buttons: ButtonConfig[] = [
- { command: "echo hello", name: "Simple Command" },
+ { id: "simple-cmd", command: "echo hello", name: "Simple Command" },
{
+ id: "cmd-group",
group: [
- { command: "ls", name: "Group Command 1" },
- { command: "pwd", name: "Group Command 2" },
+ { id: "group-cmd-1", command: "ls", name: "Group Command 1" },
+ { id: "group-cmd-2", command: "pwd", name: "Group Command 2" },
],
name: "Command Group",
},
{
+ id: "vscode-cmd",
command: "workbench.action.reload",
name: "VS Code Command",
useVsCodeApi: true,
@@ -172,12 +179,14 @@ describe("command-tree-provider", () => {
it("should verify correct item types and labels", () => {
const buttons: ButtonConfig[] = [
{
+ id: "terminal-cmd",
command: "npm run build",
name: "Terminal Command",
terminalName: "build-terminal",
},
{
- group: [{ command: "npm start", name: "Start Server" }],
+ id: "dev-tools",
+ group: [{ id: "start-server", command: "npm start", name: "Start Server" }],
name: "Development Tools",
},
];
@@ -211,8 +220,8 @@ describe("command-tree-provider", () => {
it("should handle buttons with no command property", () => {
const buttons: ButtonConfig[] = [
- { name: "Invalid Button" } as ButtonConfig,
- { command: "", name: "Empty Command" },
+ { id: "invalid-btn", name: "Invalid Button" } as ButtonConfig,
+ { id: "empty-cmd", command: "", name: "Empty Command" },
];
const result = createTreeItems(buttons);
@@ -227,10 +236,11 @@ describe("command-tree-provider", () => {
it("should handle nested group structures", () => {
const buttons: ButtonConfig[] = [
{
+ id: "main-group",
group: [
- { command: "echo test1", name: "Sub Command 1" },
- { command: "echo test2", name: "Sub Command 2" },
- { command: "echo test3", name: "Sub Command 3" },
+ { id: "sub-cmd-1", command: "echo test1", name: "Sub Command 1" },
+ { id: "sub-cmd-2", command: "echo test2", name: "Sub Command 2" },
+ { id: "sub-cmd-3", command: "echo test3", name: "Sub Command 3" },
],
name: "Main Group",
},
diff --git a/src/tests/config-manager.spec.ts b/src/tests/config-manager.spec.ts
index 057aaa14..d3472ce0 100644
--- a/src/tests/config-manager.spec.ts
+++ b/src/tests/config-manager.spec.ts
@@ -111,7 +111,7 @@ describe("ConfigManager", () => {
describe("updateButtonConfiguration", () => {
it("should update buttons configuration with correct target", async () => {
- const mockButtons = [{ name: "Test", command: "echo test" }];
+ const mockButtons = [{ id: "test-btn", name: "Test", command: "echo test" }];
const mockConfig = createMockConfig();
mockConfig.get.mockReturnValue(CONFIGURATION_TARGETS.WORKSPACE);
mockConfig.update.mockResolvedValue(undefined);
@@ -130,7 +130,7 @@ describe("ConfigManager", () => {
});
it("should use global target when configured", async () => {
- const mockButtons = [{ name: "Test", command: "echo test" }];
+ const mockButtons = [{ id: "test-btn", name: "Test", command: "echo test" }];
const mockConfig = createMockConfig();
mockConfig.get.mockReturnValue(CONFIGURATION_TARGETS.GLOBAL);
mockConfig.update.mockResolvedValue(undefined);
diff --git a/src/tests/show-all-commands.spec.ts b/src/tests/show-all-commands.spec.ts
index 443b09fd..6434c090 100644
--- a/src/tests/show-all-commands.spec.ts
+++ b/src/tests/show-all-commands.spec.ts
@@ -6,11 +6,13 @@ describe("show-all-commands", () => {
it("should create QuickPickItems with shortcuts", () => {
const buttons: ButtonConfig[] = [
{
+ id: "test-cmd-1",
command: "echo test1",
name: "Test Command 1",
shortcut: "1",
},
{
+ id: "test-cmd-2",
command: "echo test2",
name: "Test Command 2",
shortcut: "2",
@@ -35,10 +37,12 @@ describe("show-all-commands", () => {
it("should create QuickPickItems without shortcuts", () => {
const buttons: ButtonConfig[] = [
{
+ id: "test-cmd-1",
command: "echo test1",
name: "Test Command 1",
},
{
+ id: "test-cmd-2",
command: "echo test2",
name: "Test Command 2",
},
@@ -62,12 +66,15 @@ describe("show-all-commands", () => {
it("should handle group buttons", () => {
const buttons: ButtonConfig[] = [
{
+ id: "test-group",
group: [
{
+ id: "nested-cmd-1",
command: "echo nested1",
name: "Nested Command 1",
},
{
+ id: "nested-cmd-2",
command: "echo nested2",
name: "Nested Command 2",
},
@@ -89,8 +96,10 @@ describe("show-all-commands", () => {
it("should handle group buttons with shortcuts", () => {
const buttons: ButtonConfig[] = [
{
+ id: "test-group",
group: [
{
+ id: "nested-cmd",
command: "echo nested",
name: "Nested Command",
},
@@ -113,6 +122,7 @@ describe("show-all-commands", () => {
it("should handle buttons with no command", () => {
const buttons: ButtonConfig[] = [
{
+ id: "test-btn",
name: "Test Button",
},
];
@@ -139,6 +149,7 @@ describe("show-all-commands", () => {
it("should preserve object references", () => {
const buttons: ButtonConfig[] = [
{
+ id: "test-preserve",
command: "echo test",
name: "Test Command",
},
@@ -152,13 +163,16 @@ describe("show-all-commands", () => {
it("should handle mixed configurations", () => {
const buttons: ButtonConfig[] = [
{
+ id: "cmd-btn",
command: "echo command",
name: "Command Button",
shortcut: "c",
},
{
+ id: "group-btn",
group: [
{
+ id: "nested-cmd",
command: "echo nested",
name: "Nested",
},
@@ -166,6 +180,7 @@ describe("show-all-commands", () => {
name: "Group Button",
},
{
+ id: "empty-btn",
name: "Empty Button",
},
];
diff --git a/src/tests/status-bar-manager.spec.ts b/src/tests/status-bar-manager.spec.ts
index 295a2a6a..e1b13d02 100644
--- a/src/tests/status-bar-manager.spec.ts
+++ b/src/tests/status-bar-manager.spec.ts
@@ -41,9 +41,10 @@ describe("status-bar-manager", () => {
describe("createTooltipText", () => {
it("should return button name with options text for group buttons", () => {
const button: ButtonConfig = {
+ id: "test-group",
group: [
- { command: "echo test1", name: "Child 1" },
- { command: "echo test2", name: "Child 2" },
+ { id: "child-1", command: "echo test1", name: "Child 1" },
+ { id: "child-2", command: "echo test2", name: "Child 2" },
],
name: "Test Group",
};
@@ -54,6 +55,7 @@ describe("status-bar-manager", () => {
it("should return command when button has command and no group", () => {
const button: ButtonConfig = {
+ id: "test-btn",
command: "echo hello",
name: "Test Button",
};
@@ -64,6 +66,7 @@ describe("status-bar-manager", () => {
it("should return button name when button has no command and no group", () => {
const button: ButtonConfig = {
+ id: "test-btn",
name: "Test Button",
};
@@ -73,6 +76,7 @@ describe("status-bar-manager", () => {
it("should return button name with options text for empty group", () => {
const button: ButtonConfig = {
+ id: "empty-group",
group: [],
name: "Empty Group",
};
@@ -83,8 +87,9 @@ describe("status-bar-manager", () => {
it("should prioritize group over command when both exist", () => {
const button: ButtonConfig = {
+ id: "mixed-btn",
command: "echo test",
- group: [{ command: "echo child", name: "Child" }],
+ group: [{ id: "child-1", command: "echo child", name: "Child" }],
name: "Mixed Button",
};
@@ -96,6 +101,7 @@ describe("status-bar-manager", () => {
describe("createButtonCommand", () => {
it("should create command object with correct structure for command button", () => {
const button: ButtonConfig = {
+ id: "test-btn",
command: "echo hello",
name: "Test Button",
};
@@ -111,9 +117,10 @@ describe("status-bar-manager", () => {
it("should create command object with correct structure for group button", () => {
const button: ButtonConfig = {
+ id: "test-group",
group: [
- { command: "echo test1", name: "Child 1" },
- { command: "echo test2", name: "Child 2" },
+ { id: "child-1", command: "echo test1", name: "Child 1" },
+ { id: "child-2", command: "echo test2", name: "Child 2" },
],
name: "Test Group",
};
@@ -129,6 +136,7 @@ describe("status-bar-manager", () => {
it("should preserve button object reference in arguments", () => {
const button: ButtonConfig = {
+ id: "ref-test",
command: "echo reference",
name: "Reference Test",
};
@@ -140,6 +148,7 @@ describe("status-bar-manager", () => {
it("should handle button with no command or group", () => {
const button: ButtonConfig = {
+ id: "minimal-btn",
name: "Minimal Button",
};
@@ -154,6 +163,7 @@ describe("status-bar-manager", () => {
it("should handle button with additional properties", () => {
const button: ButtonConfig = {
+ id: "complex-btn",
color: "#FF0000",
command: "echo test",
name: "Complex Button",
diff --git a/src/tests/ui-items.spec.ts b/src/tests/ui-items.spec.ts
index ff9fe1b2..a1a21014 100644
--- a/src/tests/ui-items.spec.ts
+++ b/src/tests/ui-items.spec.ts
@@ -40,6 +40,7 @@ describe("ui-items", () => {
describe("createQuickPickItem", () => {
it("should create QuickPickItem without shortcut", () => {
const button: ButtonConfig = {
+ id: "test-cmd",
command: "echo 'test'",
name: "Test Command",
};
@@ -53,6 +54,7 @@ describe("ui-items", () => {
it("should create QuickPickItem with shortcut", () => {
const button: ButtonConfig = {
+ id: "test-cmd",
command: "echo 'test'",
name: "Test Command",
shortcut: "t",
@@ -66,9 +68,10 @@ describe("ui-items", () => {
it("should create QuickPickItem for group button without command count", () => {
const button: ButtonConfig = {
+ id: "group-btn",
group: [
- { command: "cmd1", name: "Command 1" },
- { command: "cmd2", name: "Command 2" },
+ { id: "cmd-1", command: "cmd1", name: "Command 1" },
+ { id: "cmd-2", command: "cmd2", name: "Command 2" },
],
name: "Group",
};
@@ -81,10 +84,11 @@ describe("ui-items", () => {
it("should create QuickPickItem for group button with command count", () => {
const button: ButtonConfig = {
+ id: "group-btn",
group: [
- { command: "cmd1", name: "Command 1" },
- { command: "cmd2", name: "Command 2" },
- { command: "cmd3", name: "Command 3" },
+ { id: "cmd-1", command: "cmd1", name: "Command 1" },
+ { id: "cmd-2", command: "cmd2", name: "Command 2" },
+ { id: "cmd-3", command: "cmd3", name: "Command 3" },
],
name: "Group",
};
@@ -99,8 +103,8 @@ describe("ui-items", () => {
describe("createQuickPickItems", () => {
it("should create multiple QuickPickItems", () => {
const buttons: ButtonConfig[] = [
- { command: "cmd1", name: "Command 1", shortcut: "1" },
- { command: "cmd2", name: "Command 2" },
+ { id: "cmd-1", command: "cmd1", name: "Command 1", shortcut: "1" },
+ { id: "cmd-2", command: "cmd2", name: "Command 2" },
];
const items = createQuickPickItems(buttons);
@@ -113,9 +117,10 @@ describe("ui-items", () => {
it("should include command count when requested", () => {
const buttons: ButtonConfig[] = [
{
+ id: "group-btn",
group: [
- { command: "cmd1", name: "Sub 1" },
- { command: "cmd2", name: "Sub 2" },
+ { id: "sub-1", command: "cmd1", name: "Sub 1" },
+ { id: "sub-2", command: "cmd2", name: "Sub 2" },
],
name: "Group",
},
@@ -155,8 +160,8 @@ describe("ui-items", () => {
describe("GroupTreeItem", () => {
it("should create GroupTreeItem", () => {
const commands: ButtonConfig[] = [
- { command: "cmd1", name: "Command 1" },
- { command: "cmd2", name: "Command 2" },
+ { id: "cmd-1", command: "cmd1", name: "Command 1" },
+ { id: "cmd-2", command: "cmd2", name: "Command 2" },
];
const item = new GroupTreeItem("Group", commands);
@@ -172,6 +177,7 @@ describe("ui-items", () => {
describe("createTreeItem", () => {
it("should create CommandTreeItem for regular button", () => {
const button: ButtonConfig = {
+ id: "test-btn",
command: "echo test",
name: "Test",
terminalName: "term1",
@@ -191,7 +197,8 @@ describe("ui-items", () => {
it("should create GroupTreeItem for group button", () => {
const button: ButtonConfig = {
- group: [{ command: "cmd1", name: "Command 1" }],
+ id: "group-btn",
+ group: [{ id: "cmd-1", command: "cmd1", name: "Command 1" }],
name: "Group",
};
@@ -206,6 +213,7 @@ describe("ui-items", () => {
it("should include parent path in buttonId", () => {
const button: ButtonConfig = {
+ id: "test-btn",
command: "echo test",
name: "Test",
};
@@ -220,6 +228,7 @@ describe("ui-items", () => {
it("should handle button without command", () => {
const button: ButtonConfig = {
+ id: "empty-btn",
name: "Empty",
};
@@ -235,9 +244,9 @@ describe("ui-items", () => {
describe("createTreeItems", () => {
it("should create multiple tree items", () => {
const buttons: ButtonConfig[] = [
- { command: "cmd1", name: "Command 1" },
- { group: [{ command: "sub", name: "Sub" }], name: "Group" },
- { command: "cmd2", name: "Command 2" },
+ { id: "cmd-1", command: "cmd1", name: "Command 1" },
+ { id: "group-1", group: [{ id: "sub-1", command: "sub", name: "Sub" }], name: "Group" },
+ { id: "cmd-2", command: "cmd2", name: "Command 2" },
];
const items = createTreeItems(buttons);
@@ -249,7 +258,7 @@ describe("ui-items", () => {
});
it("should pass parent path to each item", () => {
- const buttons: ButtonConfig[] = [{ command: "cmd", name: "Test" }];
+ const buttons: ButtonConfig[] = [{ id: "test-cmd", command: "cmd", name: "Test" }];
const items = createTreeItems(buttons, "Parent");
diff --git a/src/tests/utils-ensure-id.spec.ts b/src/tests/utils-ensure-id.spec.ts
new file mode 100644
index 00000000..322a0269
--- /dev/null
+++ b/src/tests/utils-ensure-id.spec.ts
@@ -0,0 +1,217 @@
+import { ensureId, ensureIdsInArray } from "../internal/utils/ensure-id";
+import { ButtonConfig } from "../pkg/types";
+
+describe("ensureId", () => {
+ describe("ensureId", () => {
+ it("should add ID to ButtonConfig without ID", () => {
+ const config = {
+ name: "Test Button",
+ command: "echo test",
+ } as ButtonConfig;
+
+ const result = ensureId(config);
+
+ expect(result.id).toBeDefined();
+ expect(typeof result.id).toBe("string");
+ expect(result.id).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i);
+ });
+
+ it("should preserve existing ID", () => {
+ const existingId = "existing-id-123";
+ const config: ButtonConfig = {
+ name: "Test Button",
+ command: "echo test",
+ id: existingId,
+ };
+
+ const result = ensureId(config);
+
+ expect(result.id).toBe(existingId);
+ });
+
+ it("should preserve all other properties", () => {
+ const config = {
+ name: "Test Button",
+ command: "echo test",
+ color: "#FF0000",
+ shortcut: "ctrl+t",
+ terminalName: "TestTerminal",
+ useVsCodeApi: true,
+ executeAll: false,
+ } as ButtonConfig;
+
+ const result = ensureId(config);
+
+ expect(result.name).toBe(config.name);
+ expect(result.command).toBe(config.command);
+ expect(result.color).toBe(config.color);
+ expect(result.shortcut).toBe(config.shortcut);
+ expect(result.terminalName).toBe(config.terminalName);
+ expect(result.useVsCodeApi).toBe(config.useVsCodeApi);
+ expect(result.executeAll).toBe(config.executeAll);
+ });
+
+ it("should recursively add IDs to group items", () => {
+ const config = {
+ name: "Parent Group",
+ group: [
+ { name: "Child 1", command: "echo 1" },
+ { name: "Child 2", command: "echo 2" },
+ ],
+ } as ButtonConfig;
+
+ const result = ensureId(config);
+
+ expect(result.id).toBeDefined();
+ expect(result.group).toBeDefined();
+ expect(result.group![0].id).toBeDefined();
+ expect(result.group![1].id).toBeDefined();
+ expect(result.group![0].id).not.toBe(result.group![1].id);
+ });
+
+ it("should handle deeply nested groups", () => {
+ const config = {
+ name: "Level 1",
+ group: [
+ {
+ name: "Level 2",
+ group: [
+ {
+ name: "Level 3",
+ command: "echo deep",
+ },
+ ],
+ },
+ ],
+ } as ButtonConfig;
+
+ const result = ensureId(config);
+
+ expect(result.id).toBeDefined();
+ expect(result.group![0].id).toBeDefined();
+ expect(result.group![0].group![0].id).toBeDefined();
+ });
+
+ it("should preserve existing IDs in nested groups", () => {
+ const parentId = "parent-id";
+ const childId = "child-id";
+ const config: ButtonConfig = {
+ name: "Parent",
+ id: parentId,
+ group: [
+ {
+ name: "Child",
+ id: childId,
+ command: "echo test",
+ },
+ ],
+ };
+
+ const result = ensureId(config);
+
+ expect(result.id).toBe(parentId);
+ expect(result.group![0].id).toBe(childId);
+ });
+
+ it("should handle empty group array", () => {
+ const config = {
+ name: "Empty Group",
+ group: [] as ButtonConfig[],
+ } as ButtonConfig;
+
+ const result = ensureId(config);
+
+ expect(result.id).toBeDefined();
+ expect(result.group).toEqual([]);
+ });
+
+ it("should generate unique IDs for multiple calls", () => {
+ const config = {
+ name: "Test Button",
+ command: "echo test",
+ } as ButtonConfig;
+
+ const result1 = ensureId(config);
+ const result2 = ensureId(config);
+
+ expect(result1.id).toBeDefined();
+ expect(result2.id).toBeDefined();
+ expect(result1.id).not.toBe(result2.id);
+ });
+ });
+
+ describe("ensureIdsInArray", () => {
+ it("should add IDs to all configs in array", () => {
+ const configs = [
+ { name: "Button 1", command: "echo 1" },
+ { name: "Button 2", command: "echo 2" },
+ { name: "Button 3", command: "echo 3" },
+ ] as ButtonConfig[];
+
+ const result = ensureIdsInArray(configs);
+
+ expect(result).toHaveLength(3);
+ expect(result[0].id).toBeDefined();
+ expect(result[1].id).toBeDefined();
+ expect(result[2].id).toBeDefined();
+ expect(result[0].id).not.toBe(result[1].id);
+ expect(result[1].id).not.toBe(result[2].id);
+ });
+
+ it("should handle empty array", () => {
+ const configs: ButtonConfig[] = [];
+
+ const result = ensureIdsInArray(configs);
+
+ expect(result).toEqual([]);
+ });
+
+ it("should preserve existing IDs in array", () => {
+ const id1 = "existing-1";
+ const id2 = "existing-2";
+ const configs: ButtonConfig[] = [
+ { name: "Button 1", command: "echo 1", id: id1 },
+ { name: "Button 2", command: "echo 2", id: id2 },
+ ];
+
+ const result = ensureIdsInArray(configs);
+
+ expect(result[0].id).toBe(id1);
+ expect(result[1].id).toBe(id2);
+ });
+
+ it("should handle mixed configs with and without IDs", () => {
+ const existingId = "existing-id";
+ const configs = [
+ { name: "Button 1", command: "echo 1", id: existingId },
+ { name: "Button 2", command: "echo 2" },
+ ] as ButtonConfig[];
+
+ const result = ensureIdsInArray(configs);
+
+ expect(result[0].id).toBe(existingId);
+ expect(result[1].id).toBeDefined();
+ expect(result[1].id).not.toBe(existingId);
+ });
+
+ it("should recursively process nested groups in array", () => {
+ const configs = [
+ {
+ name: "Group 1",
+ group: [{ name: "Child 1", command: "echo 1" }],
+ },
+ {
+ name: "Group 2",
+ group: [{ name: "Child 2", command: "echo 2" }],
+ },
+ ] as ButtonConfig[];
+
+ const result = ensureIdsInArray(configs);
+
+ expect(result[0].id).toBeDefined();
+ expect(result[0].group![0].id).toBeDefined();
+ expect(result[1].id).toBeDefined();
+ expect(result[1].group![0].id).toBeDefined();
+ });
+ });
+});
diff --git a/src/view/src/components/command-form.tsx b/src/view/src/components/command-form.tsx
index 787a5fb1..4da6cdd0 100644
--- a/src/view/src/components/command-form.tsx
+++ b/src/view/src/components/command-form.tsx
@@ -19,6 +19,7 @@ export const CommandForm = ({ command, formId, onSave }: CommandFormProps) => {
command: "",
executeAll: false,
group: [],
+ id: crypto.randomUUID(),
name: "",
shortcut: "",
terminalName: "",
@@ -48,6 +49,7 @@ export const CommandForm = ({ command, formId, onSave }: CommandFormProps) => {
const saveCommand = () => {
const commandConfig: ButtonConfig = {
color: formData.color || undefined,
+ id: formData.id,
name: formData.name.trim(),
shortcut: formData.shortcut || undefined,
};
diff --git a/src/view/src/components/command-list.tsx b/src/view/src/components/command-list.tsx
index 70bd30dc..f43749f7 100644
--- a/src/view/src/components/command-list.tsx
+++ b/src/view/src/components/command-list.tsx
@@ -2,12 +2,12 @@ import { GripVertical } from "lucide-react";
import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "~/core";
+import { DeleteConfirmationDialog } from "./delete-confirmation-dialog";
import { useCommandForm } from "../context/command-form-context.tsx";
import { useVscodeCommand } from "../context/vscode-command-context.tsx";
import { useSortableItem } from "../hooks/use-sortable-item";
import { useSortableList } from "../hooks/use-sortable-list";
import { type ButtonConfig } from "../types";
-import { DeleteConfirmationDialog } from "./delete-confirmation-dialog";
export const CommandList = () => {
const { commands, reorderCommands } = useVscodeCommand();
@@ -31,7 +31,7 @@ export const CommandList = () => {
{commands.map((command, index) => (
-
+
))}
diff --git a/src/view/src/components/group-command-list.tsx b/src/view/src/components/group-command-list.tsx
index d9479e03..0cd803b0 100644
--- a/src/view/src/components/group-command-list.tsx
+++ b/src/view/src/components/group-command-list.tsx
@@ -57,17 +57,17 @@ export const GroupCommandList = ({
const sensors = useMemo(() => [pointerSensor, keyboardSensor], [pointerSensor, keyboardSensor]);
- const sortableItemIds = useMemo(() => commands.map((_, index) => `${index}`), [commands]);
+ const sortableItemIds = useMemo(() => commands.map((command) => command.id), [commands]);
const handleDragEnd = useCallback(
(event: DragEndEvent) => {
const { active, over } = event;
if (over && active.id !== over.id) {
- const oldIndex = Number(active.id);
- const newIndex = Number(over.id);
+ const oldIndex = commands.findIndex((cmd) => cmd.id === active.id);
+ const newIndex = commands.findIndex((cmd) => cmd.id === over.id);
- if (!isNaN(oldIndex) && !isNaN(newIndex)) {
+ if (oldIndex !== -1 && newIndex !== -1) {
const newItems = arrayMove(commands, oldIndex, newIndex);
onChange(newItems);
}
@@ -90,9 +90,9 @@ export const GroupCommandList = ({
{commands.map((command, index) => (
onEditGroup(index) : undefined}
onUpdate={updateCommand}
diff --git a/src/view/src/hooks/use-command-operations.tsx b/src/view/src/hooks/use-command-operations.tsx
index ea7ee37b..de7d6640 100644
--- a/src/view/src/hooks/use-command-operations.tsx
+++ b/src/view/src/hooks/use-command-operations.tsx
@@ -26,6 +26,7 @@ export const useCommandOperations = (
const addCommand = useCallback(() => {
const newCommand: ButtonConfig = {
command: "",
+ id: crypto.randomUUID(),
name: "",
useVsCodeApi: false,
};
@@ -36,6 +37,7 @@ export const useCommandOperations = (
const newGroup: ButtonConfig = {
executeAll: false,
group: [],
+ id: crypto.randomUUID(),
name: "",
};
onChange([...commands, newGroup]);
diff --git a/src/view/src/hooks/use-sortable-list.tsx b/src/view/src/hooks/use-sortable-list.tsx
index bce4f8f6..05f34f6d 100644
--- a/src/view/src/hooks/use-sortable-list.tsx
+++ b/src/view/src/hooks/use-sortable-list.tsx
@@ -39,8 +39,8 @@ export const useSortableList = ({ items, onReorder }: UseSortableListProps) => {
const { active, over } = event;
if (over && active.id !== over.id) {
- const oldIndex = Number(active.id);
- const newIndex = Number(over.id);
+ const oldIndex = items.findIndex((item) => item.id === active.id);
+ const newIndex = items.findIndex((item) => item.id === over.id);
if (oldIndex !== -1 && newIndex !== -1) {
const newItems = arrayMove(items, oldIndex, newIndex);
@@ -53,10 +53,7 @@ export const useSortableList = ({ items, onReorder }: UseSortableListProps) => {
const SortableWrapper = ({ children }: { children: React.ReactNode }) => (
- `${index}`)}
- strategy={verticalListSortingStrategy}
- >
+ item.id)} strategy={verticalListSortingStrategy}>
{children}
diff --git a/src/view/src/mock/mock-data.tsx b/src/view/src/mock/mock-data.tsx
index 8b12a4e8..ebcbac3a 100644
--- a/src/view/src/mock/mock-data.tsx
+++ b/src/view/src/mock/mock-data.tsx
@@ -4,6 +4,7 @@ export const mockCommands: ButtonConfig[] = [
{
color: "#4CAF50",
command: "npm test",
+ id: "mock-test-command",
name: "$(testing-passed-icon) Test",
shortcut: "t",
terminalName: "Test Runner",
@@ -11,6 +12,7 @@ export const mockCommands: ButtonConfig[] = [
{
color: "#00BCD4",
command: "workbench.action.terminal.new",
+ id: "mock-terminal-command",
name: "$(terminal) Terminal",
shortcut: "n",
useVsCodeApi: true,
@@ -20,11 +22,13 @@ export const mockCommands: ButtonConfig[] = [
group: [
{
command: "git pull",
+ id: "mock-git-pull",
name: "$(arrow-down) Pull",
shortcut: "l",
},
{
command: "git push",
+ id: "mock-git-push",
name: "$(arrow-up) Push",
shortcut: "p",
},
@@ -32,24 +36,29 @@ export const mockCommands: ButtonConfig[] = [
group: [
{
command: "git status",
+ id: "mock-git-status",
name: "$(git-commit) Status",
shortcut: "s",
},
{
command: "git diff",
+ id: "mock-git-diff",
name: "$(diff) Diff",
shortcut: "d",
},
{
command: "git log --oneline -5",
+ id: "mock-git-log",
name: "$(history) Log",
shortcut: "l",
},
],
+ id: "mock-git-check-status",
name: "$(search) Check Status",
shortcut: "c",
},
],
+ id: "mock-git-group",
name: "$(git-branch) Git",
shortcut: "g",
},