From 09afffeead2582249faf93878ba89c885d316055 Mon Sep 17 00:00:00 2001 From: Brendan Allan <14191578+Brendonovich@users.noreply.github.com> Date: Mon, 27 Jul 2026 11:45:18 +0800 Subject: [PATCH] fix(app): preserve shadowed command owners (#39044) --- packages/app/src/context/command.test.ts | 29 ++++++++++++++++++------ packages/app/src/context/command.tsx | 19 ++++++++++++---- 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/packages/app/src/context/command.test.ts b/packages/app/src/context/command.test.ts index cf98ff38251..e0d90575113 100644 --- a/packages/app/src/context/command.test.ts +++ b/packages/app/src/context/command.test.ts @@ -1,5 +1,11 @@ import { describe, expect, test } from "bun:test" -import { commandPaletteOptions, resolveKeybindOption, upsertCommandRegistration, type CommandOption } from "./command" +import { + activeCommandRegistrations, + addCommandRegistration, + commandPaletteOptions, + resolveKeybindOption, + type CommandOption, +} from "./command" const paletteOptions: CommandOption[] = [ { id: "settings.open", title: "Open settings" }, @@ -15,22 +21,31 @@ describe("commandPaletteOptions", () => { }) }) -describe("upsertCommandRegistration", () => { - test("replaces keyed registrations", () => { +describe("command registrations", () => { + test("shadows keyed registrations while retaining the previous owner", () => { const one = () => [{ id: "one", title: "One" }] const two = () => [{ id: "two", title: "Two" }] - const next = upsertCommandRegistration([{ key: "layout", options: one }], { key: "layout", options: two }) + const registrations = addCommandRegistration([{ key: "layout", options: one }], { + key: "layout", + options: two, + }) + const active = activeCommandRegistrations(registrations) - expect(next).toHaveLength(1) - expect(next[0]?.options).toBe(two) + expect(registrations).toHaveLength(2) + expect(active).toHaveLength(1) + expect(active[0]?.options).toBe(two) + + const restored = activeCommandRegistrations(registrations.filter((entry) => entry.options !== two)) + expect(restored).toHaveLength(1) + expect(restored[0]?.options).toBe(one) }) test("keeps unkeyed registrations additive", () => { const one = () => [{ id: "one", title: "One" }] const two = () => [{ id: "two", title: "Two" }] - const next = upsertCommandRegistration([{ options: one }], { options: two }) + const next = activeCommandRegistrations(addCommandRegistration([{ options: one }], { options: two })) expect(next).toHaveLength(2) expect(next[0]?.options).toBe(two) diff --git a/packages/app/src/context/command.tsx b/packages/app/src/context/command.tsx index f88033bd7e2..054b99e806b 100644 --- a/packages/app/src/context/command.tsx +++ b/packages/app/src/context/command.tsx @@ -114,9 +114,18 @@ export type CommandRegistration = { options: Accessor } -export function upsertCommandRegistration(registrations: CommandRegistration[], entry: CommandRegistration) { - if (entry.key === undefined) return [entry, ...registrations] - return [entry, ...registrations.filter((x) => x.key !== entry.key)] +export function addCommandRegistration(registrations: CommandRegistration[], entry: CommandRegistration) { + return [entry, ...registrations] +} + +export function activeCommandRegistrations(registrations: CommandRegistration[]) { + const keys = new Set() + return registrations.filter((entry) => { + if (entry.key === undefined) return true + if (keys.has(entry.key)) return false + keys.add(entry.key) + return true + }) } export function parseKeybind(config: string): Keybind[] { @@ -281,7 +290,7 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex const seen = new Set() const all: CommandOption[] = [] - for (const reg of store.registrations) { + for (const reg of activeCommandRegistrations(store.registrations)) { for (const opt of reg.options()) { if (seen.has(opt.id)) { if (import.meta.env.DEV && !warnedDuplicates.has(opt.id)) { @@ -425,7 +434,7 @@ export const { use: useCommand, provider: CommandProvider } = createSimpleContex key: id, options, } - setStore("registrations", (arr) => upsertCommandRegistration(arr, entry)) + setStore("registrations", (arr) => addCommandRegistration(arr, entry)) onCleanup(() => { setStore("registrations", (arr) => arr.filter((x) => x !== entry)) })