Blockquotes: the callout override had displaced Streamdown's own styled blockquote component, so plain quotes rendered as bare unstyled text since that commit. The pass-through branch now restores Streamdown's verbatim classes (border-l-4, muted italic). Explanations tucked for ALL questions (owner): the prediction's Why / Hide-why toggle is universal — every quiz answered card sits its explanation behind the same collapsed button. Same pattern, one law. The thinking row's rhythm: my-1 -> my-3. The 4px margin hugged the options on non-graded answered cards (owner report); 12px matches the card's mt-2/mt-3 spacing grammar everywhere the row renders. Side-based authorship (owner round, 2026-10-07): the document shape blurred user into agent — three left-rail idioms (user text, user thinking, quoted prose) collided once quotes rendered. User rows go RIGHT: 'You' messages and practice thinking cards hug the right edge at 85% width with the rail mirrored (border-r), text still reading left-to-right. In-card thinking (inside the answered card, the agent's context) stays left. Transcript thinking rows arrive EXPANDED — the thinking was clearly sent. Cmd+T (toggle-user-thinking, chat scope, cheat-sheet row) sets every user-thinking row to one state via a module store: explicit sets stomp individual toggles, late mounts honor the last set (the state initializer reads the store, so static render reflects it too).
57 lines
2.3 KiB
TypeScript
57 lines
2.3 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { describe, it } from "node:test";
|
|
import {
|
|
actionForBinding,
|
|
bindingForAction,
|
|
formatBinding,
|
|
kbdEventForAction,
|
|
} from "../src/core/keyboard.ts";
|
|
import { kbdEventForAction as kbdEventForActionFromUi } from "../components/keyboard-shortcuts.tsx";
|
|
|
|
/**
|
|
* The ⌘B sidebar bug interlude (owner, 2026-09-25): ⌘B did nothing because
|
|
* the sidebar listened on the pre-convention "lh:toggle-sidebar" while the
|
|
* dispatcher sent "lh:kbd-toggle-sidebar". The fix is structural — both
|
|
* sides compose the event name through the ONE helper — and these pins
|
|
* hold the chain together: the registry maps the keypress to the action,
|
|
* the helper spells the event, and the UI dispatcher imports the same
|
|
* helper (not its own copy).
|
|
*/
|
|
|
|
describe("kbdEventForAction — the one spelling for action events", () => {
|
|
it("dispatch and listen names are the helper's output", () => {
|
|
assert.equal(kbdEventForAction("toggle-sidebar"), "lh:kbd-toggle-sidebar");
|
|
assert.equal(kbdEventForAction("sessions-search"), "lh:kbd-sessions-search");
|
|
});
|
|
|
|
it("the dispatcher imports the same helper (no second spelling exists)", () => {
|
|
assert.equal(kbdEventForActionFromUi, kbdEventForAction);
|
|
});
|
|
});
|
|
|
|
describe("the ⌘B chain — keypress → binding → action → event", () => {
|
|
it("mod+t is the toggle-user-thinking action (owner round, 2026-10-07)", () => {
|
|
assert.equal(actionForBinding("mod+t"), "toggle-user-thinking");
|
|
assert.equal(bindingForAction("toggle-user-thinking"), "mod+t");
|
|
assert.equal(kbdEventForAction("toggle-user-thinking"), "lh:kbd-toggle-user-thinking");
|
|
});
|
|
|
|
it("mod+b is the toggle-sidebar action", () => {
|
|
assert.equal(actionForBinding("mod+b"), "toggle-sidebar");
|
|
assert.equal(bindingForAction("toggle-sidebar"), "mod+b");
|
|
assert.equal(formatBinding("mod+b"), "⌘B");
|
|
});
|
|
|
|
it("the event the chain ends in is the one the sidebar answers on", () => {
|
|
const action = actionForBinding("mod+b");
|
|
assert.equal(action, "toggle-sidebar");
|
|
assert.equal(kbdEventForAction(action), "lh:kbd-toggle-sidebar");
|
|
});
|
|
});
|
|
|
|
describe("the thinking pill suffix — ⇧⇥ via the registry", () => {
|
|
it("shift+tab formats as ⇧⇥ and maps to cycle-thinking", () => {
|
|
assert.equal(actionForBinding("shift+tab"), "cycle-thinking");
|
|
assert.equal(formatBinding("shift+tab"), "⇧⇥");
|
|
});
|
|
}); |