feat(nexus): add CmdKButton shim for layout overhaul (phase 8)
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) <noreply@anthropic.com>
This commit is contained in:
parent
1de78f855f
commit
c1647e70d7
2 changed files with 99 additions and 0 deletions
62
ui/src/components/frame/CmdKButton.test.tsx
Normal file
62
ui/src/components/frame/CmdKButton.test.tsx
Normal file
|
|
@ -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<typeof createRoot> | 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(<CmdKButton />);
|
||||||
|
});
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
37
ui/src/components/frame/CmdKButton.tsx
Normal file
37
ui/src/components/frame/CmdKButton.tsx
Normal file
|
|
@ -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 (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleClick}
|
||||||
|
aria-label="Open command palette"
|
||||||
|
title="Open command palette (⌘K)"
|
||||||
|
className="inline-flex h-8 items-center gap-2 rounded-[4px] border border-border bg-card px-2 text-[12px] font-medium text-muted-foreground transition-colors hover:text-primary hover:border-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:ring-offset-background"
|
||||||
|
>
|
||||||
|
<kbd className="font-mono text-[12px]">⌘K</kbd>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Add table
Reference in a new issue