Skip to content

Commit 5ebe95f

Browse files
committed
Pin live restore of disabled plugin commands
Disabled command plugins must already sit in the candidate list so enablement can restore slash commands without a second register pass.
1 parent 686f9ea commit 5ebe95f

3 files changed

Lines changed: 71 additions & 1 deletion

File tree

src/tui/command-catalog.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* Pure: host injects `listCommands()` results (or fixtures). No registry import
55
* here — avoids circular / heavy deps from `src/tui/commands`.
66
*
7-
* setPaletteCatalog(shell, commandItemsFromRegistry(listCommands()))
7+
* setPaletteCatalog(shell, () => commandItemsFromRegistry(listCommands()))
88
*/
99

1010
import { sliceToWidth, stringWidth } from "./view/height.js";

src/tui/commands/registry.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,9 +141,67 @@ describe("registerCommandPlugin", () => {
141141
expect(getCommand("live-plugin-cmd")).toBeUndefined();
142142

143143
active = true;
144+
expect(listCommands().map((command) => command.name)).toContain("live-plugin-cmd");
144145
expect(getCommand("live-plugin-cmd")).toBeDefined();
145146
});
146147

148+
it("serves the next candidate when the first plugin deactivates", () => {
149+
let firstActive = true;
150+
registerCommandPlugin(
151+
{
152+
commands: [
153+
{
154+
name: "plugin-live-fallback",
155+
description: "first",
156+
handler: () => ({ type: "noop" }),
157+
},
158+
],
159+
},
160+
() => firstActive,
161+
);
162+
registerCommandPlugin({
163+
commands: [
164+
{
165+
name: "plugin-live-fallback",
166+
description: "second",
167+
handler: () => ({ type: "noop" }),
168+
},
169+
],
170+
});
171+
172+
expect(getCommand("plugin-live-fallback")?.description).toBe("first");
173+
expect(
174+
listCommands().find((command) => command.name === "plugin-live-fallback")?.description,
175+
).toBe("first");
176+
177+
firstActive = false;
178+
expect(getCommand("plugin-live-fallback")?.description).toBe("second");
179+
expect(
180+
listCommands().find((command) => command.name === "plugin-live-fallback")?.description,
181+
).toBe("second");
182+
});
183+
184+
it("does not execute a typed slash name after the plugin deactivates", () => {
185+
let active = true;
186+
registerCommandPlugin(
187+
{
188+
commands: [
189+
{
190+
name: "typed-after-disable",
191+
description: "typed",
192+
handler: () => ({ type: "message", text: "ran" }),
193+
},
194+
],
195+
},
196+
() => active,
197+
);
198+
199+
const stalePaletteRow = "typed-after-disable";
200+
expect(getCommand(stalePaletteRow)).toBeDefined();
201+
active = false;
202+
expect(getCommand(stalePaletteRow)).toBeUndefined();
203+
});
204+
147205
it("lets an enabled plugin claim a name ahead of a disabled candidate", () => {
148206
registerCommandPlugin(
149207
{

tests/unit/plugin-register.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
isPluginModuleEnabled,
88
} from "../../src/plugins/register.js";
99
import type { PluginModule } from "../../src/plugins/loader.js";
10+
import { getCommand } from "../../src/tui/commands/registry.js";
1011

1112
function cmdModule(id: string, extra: Partial<PluginModule> = {}): PluginModule {
1213
return {
@@ -55,6 +56,17 @@ test("registerCommandPlugins registers only enabled command plugins", () => {
5556
expect(registered).toEqual(["reg-on"]);
5657
});
5758

59+
test("registerCommandPlugins restores a disabled-at-startup command without re-registering", () => {
60+
const mods = [cmdModule("reg-off-live")];
61+
const config: Record<string, { enabled: boolean }> = { "reg-off-live": { enabled: false } };
62+
const registered = registerCommandPlugins(mods, config);
63+
expect(registered).toEqual([]);
64+
expect(getCommand("reg-off-live")).toBeUndefined();
65+
66+
config["reg-off-live"] = { enabled: true };
67+
expect(getCommand("reg-off-live")).toBeDefined();
68+
});
69+
5870
test("enablePluginConfig marks enabled and preserves credentials/consented", () => {
5971
// Path-add must leave plugins[id].enabled === true so restart re-wires commands.
6072
expect(isPluginEnabled({}, "path-cmd")).toBe(false);

0 commit comments

Comments
 (0)