From c1647e70d70dbe20f2a2b1341bcf82a303f7fdb2 Mon Sep 17 00:00:00 2001 From: Nexus Dev Date: Sat, 11 Apr 2026 11:12:55 +0000 Subject: [PATCH] feat(nexus): add CmdKButton shim for layout overhaul (phase 8) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Top-strip button that renders the ⌘K glyph and opens the existing CommandPalette by dispatching a synthetic Meta+K keydown on document, which CommandPalette.tsx already listens for at its useEffect (lines 42-51). This is explicitly a Phase 8 shim; Phase 14 of docs/specs/2026-04-11-nexus-layout-overhaul.md replaces it with a proper command-palette context when globalizing the palette's search index. Part of Phase 8 of the Nexus layout overhaul (task 3 of 7). Co-Authored-By: Claude Opus 4.6 (1M context) --- ui/src/components/frame/CmdKButton.test.tsx | 62 +++++++++++++++++++++ ui/src/components/frame/CmdKButton.tsx | 37 ++++++++++++ 2 files changed, 99 insertions(+) create mode 100644 ui/src/components/frame/CmdKButton.test.tsx create mode 100644 ui/src/components/frame/CmdKButton.tsx diff --git a/ui/src/components/frame/CmdKButton.test.tsx b/ui/src/components/frame/CmdKButton.test.tsx new file mode 100644 index 00000000..a5c94f8d --- /dev/null +++ b/ui/src/components/frame/CmdKButton.test.tsx @@ -0,0 +1,62 @@ +// @vitest-environment jsdom + +import { act } from "react"; +import { createRoot } from "react-dom/client"; +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import { CmdKButton } from "./CmdKButton"; + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true; + +describe("CmdKButton", () => { + let container: HTMLDivElement; + let root: ReturnType | null = null; + + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + root = null; + }); + + afterEach(() => { + if (root) { + act(() => { root!.unmount(); }); + root = null; + } + if (container.parentNode) container.remove(); + }); + + function renderButton() { + root = createRoot(container); + act(() => { + root!.render(); + }); + return { + getButton: () => container.querySelector("button[aria-label='Open command palette']") as HTMLButtonElement, + getKbd: () => container.querySelector("kbd")?.textContent?.trim(), + }; + } + + it("renders a button with the ⌘K kbd glyph", () => { + const { getButton, getKbd } = renderButton(); + expect(getButton()).not.toBeNull(); + expect(getKbd()).toBe("⌘K"); + }); + + it("dispatches a Meta+K keydown on document when clicked", () => { + const listener = vi.fn(); + document.addEventListener("keydown", listener); + + const { getButton } = renderButton(); + act(() => { + getButton().click(); + }); + + expect(listener).toHaveBeenCalledTimes(1); + const event = listener.mock.calls[0]![0] as KeyboardEvent; + expect(event.key).toBe("k"); + expect(event.metaKey).toBe(true); + + document.removeEventListener("keydown", listener); + }); +}); diff --git a/ui/src/components/frame/CmdKButton.tsx b/ui/src/components/frame/CmdKButton.tsx new file mode 100644 index 00000000..3e6398c7 --- /dev/null +++ b/ui/src/components/frame/CmdKButton.tsx @@ -0,0 +1,37 @@ +/** + * CmdKButton — Phase 8 shim for the top-strip command palette trigger. + * + * Renders the ⌘K keyboard glyph and, when clicked, dispatches a synthetic + * Meta+K keydown event on `document`. The existing CommandPalette component + * in ui/src/components/CommandPalette.tsx installs a document-level keydown + * listener for Meta+K (see its useEffect at lines 42-51) and opens itself + * when that key is pressed, so the synthetic event reaches it without + * needing a refactor to share state. + * + * Phase 14 of docs/specs/2026-04-11-nexus-layout-overhaul.md replaces this + * shim with a proper command-palette context and globalizes the palette's + * search index. This file will either be deleted or gutted at that point. + */ +export function CmdKButton() { + const handleClick = () => { + const event = new KeyboardEvent("keydown", { + key: "k", + metaKey: true, + bubbles: true, + cancelable: true, + }); + document.dispatchEvent(event); + }; + + return ( + + ); +}