diff --git a/docs/superpowers/plans/2026-05-13-chat-debug-launcher-left.md b/docs/superpowers/plans/2026-05-13-chat-debug-launcher-left.md new file mode 100644 index 00000000..fa1e7c6a --- /dev/null +++ b/docs/superpowers/plans/2026-05-13-chat-debug-launcher-left.md @@ -0,0 +1,292 @@ +# UI Polish — Debug Toggle to Bottom-Left + Missing Sidebar Launcher Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Move the `chat-debug` toggle pill from bottom-right to bottom-left, and add the missing `` to the `chat-sidebar` composition so the sidebar mode is actually openable. + +**Architecture:** Two small surgical edits in `libs/chat`: a single CSS property change in `chat-debug.component.ts` (`right: 20px` → `left: 20px`), and a three-line addition to `chat-sidebar.component.ts` mirroring the existing pattern in `chat-popup.component.ts`. One new spec assertion to lock in the sidebar launcher render. + +**Tech Stack:** Angular 21 standalone components, signal model inputs, vitest, CSS-in-TS (template-literal styles). + +**Reference spec:** `docs/superpowers/specs/2026-05-13-chat-debug-launcher-left-design.md` + +--- + +### Task 1: Move chat-debug launcher pill to bottom-left + +**Files:** +- Modify: `libs/chat/src/lib/compositions/chat-debug/chat-debug.component.ts:52` + +**Context:** The `.launcher` rule in the inline `styles` template uses `position: fixed; bottom: 20px; right: 20px;`. We change `right` to `left`. No other change. The `.panel--right` / `.panel--left` / `.panel--bottom` dock variants reference their own viewport edges and are unaffected — they tile to whichever edge the user picks in the debug panel's dock controls. + +There is no unit test for this CSS — the style is a template literal containing CSS, and asserting that strings contain `left: 20px` rather than `right: 20px` has near-zero defect-catching value. Manual verification covers it. + +--- + +- [ ] **Step 1: Locate the rule** + +Open `libs/chat/src/lib/compositions/chat-debug/chat-debug.component.ts` and find lines ~48–67 — the `.launcher` selector inside the inline styles template. The exact block reads: + +```css + .launcher { + position: fixed; + bottom: 20px; + right: 20px; + display: inline-flex; +``` + +- [ ] **Step 2: Change `right` to `left`** + +Replace `right: 20px;` with `left: 20px;` on that one line. The resulting block: + +```css + .launcher { + position: fixed; + bottom: 20px; + left: 20px; + display: inline-flex; +``` + +Nothing else in the file changes. + +- [ ] **Step 3: Run the chat-debug spec to confirm nothing broke** + +``` +cd libs/chat && npx vitest run src/lib/compositions/chat-debug/chat-debug.component.spec.ts +``` + +Expected: PASS — existing tests don't assert on launcher position, so the change is invisible to them. + +- [ ] **Step 4: Commit** + +```bash +git add libs/chat/src/lib/compositions/chat-debug/chat-debug.component.ts +git commit -m "fix(chat-debug): move toggle pill to bottom-left + +Previously the .launcher rule anchored to bottom-right (right: 20px) +where it collided with chat-popup's chat-launcher-button (also 56×56 +at bottom-right). The debug pill rendered later in the DOM with +z-index: 990, so clicks at that corner reached debug — never the +popup launcher. Moving the debug pill to the opposite corner +(left: 20px) resolves the collision and frees up the canonical +bottom-right slot for user-facing chat launchers. + +Verified via demo.cacheplane.ai browser smoke (elementsFromPoint at +the collision point returned [debug-toggle, ..., popup-launcher]). + +Co-Authored-By: Claude Opus 4.7 (1M context) " +``` + +--- + +### Task 2: Add launcher button to chat-sidebar composition + +**Files:** +- Modify: `libs/chat/src/lib/compositions/chat-sidebar/chat-sidebar.component.ts` +- Modify: `libs/chat/src/lib/compositions/chat-sidebar/chat-sidebar.component.spec.ts` + +**Context:** `chat-popup.component.ts:62` includes `` in its template, alongside the `` composition inside the popup window. The `chat-sidebar` composition omits this entirely — there's no launcher in the DOM at all on `/sidebar`. The fix mirrors popup's pattern: add the import, register in the `imports` array, render in the template. + +The existing `toggle()` method on `ChatSidebarComponent` (line 82) flips the `open` model — exactly what the new launcher needs. The chat-launcher-button styles already position the button at `bottom: 20px; right: 20px` (the canonical user-facing position; debug is now out of the way after Task 1). + +--- + +- [ ] **Step 1: Write the failing test** + +Open `libs/chat/src/lib/compositions/chat-sidebar/chat-sidebar.component.spec.ts`. After the existing `describe('ChatSidebarComponent', () => { ... })` block's tests, add a new test (inside the same `describe`): + +```ts + it('renders a chat-launcher-button', () => { + const fixture = TestBed.createComponent(ChatSidebarComponent); + fixture.componentRef.setInput('agent', { + messages: () => [], + isLoading: () => false, + status: () => 'idle', + submit: () => Promise.resolve(), + reset: () => undefined, + }); + fixture.detectChanges(); + const launcher = fixture.nativeElement.querySelector('.chat-launcher-button'); + expect(launcher).not.toBeNull(); + }); +``` + +The minimal agent stub is enough to satisfy `agent = input.required()` — the rest of the component doesn't run during a single `detectChanges` if the panel isn't opened. If TypeScript complains about `setInput` arg types, cast: `fixture.componentRef.setInput('agent', stub as never)`. + +- [ ] **Step 2: Run to verify failure** + +``` +cd libs/chat && npx vitest run src/lib/compositions/chat-sidebar/chat-sidebar.component.spec.ts +``` + +Expected: FAIL — `expected null not to be null` on the launcher query (the button isn't rendered today). + +- [ ] **Step 3: Add the launcher to the component** + +Open `libs/chat/src/lib/compositions/chat-sidebar/chat-sidebar.component.ts`. Locate the existing imports near the top: + +```ts +import { ChatComponent } from '../chat/chat.component'; +``` + +Immediately after that line, add: + +```ts +import { ChatLauncherButtonComponent } from '../../primitives/chat-launcher-button/chat-launcher-button.component'; +``` + +Locate the `@Component({ imports: [...] })` array (it currently includes `ChatComponent`). Add `ChatLauncherButtonComponent` to the array. The result should read like: + +```ts + imports: [ChatComponent, ChatLauncherButtonComponent], +``` + +(If the file uses a different formatting — multi-line array, alphabetized, etc. — follow the existing style.) + +Now locate the template. The current template renders `
` followed by ``. The launcher belongs as a sibling of those — typically positioned before the `