Back to feed

sledtools/pika branch #53

pika-git-chat-sidebar-1

Move branch discussion into a sidebar

Target branch: master

Merge Commit: 44a55eab7589c919064857fe8f65fb01db590ddc

branch: merged tutorial: ready ci: success
Open CI Details

Continuous Integration

CI: success

Compact status on the review page, with full logs on the CI page.

Open CI Details

Latest run #65 success

2 passed

head 1790f5b08043ce146fb506c41b97891cf240cc25 · queued 2026-03-25 18:16:27 · 2 lane(s)

queued 9s · ran 28s

check-notifications · success check-agent-contracts · success

Summary

This branch moves the branch discussion panel from an inline position within the review layout into a dedicated sidebar that slides in from the right. On desktop the sidebar uses a CSS grid column transition; on mobile it becomes a fixed overlay with a backdrop. A toggle button with a keyboard shortcut (Cmd+. on Mac, Ctrl+. elsewhere) is added to the action row, and the discussion panel gains a close button, Escape-key dismissal, and proper ARIA attributes. The Rust test file is updated to assert the presence of the new structural element IDs.

Tutorial Steps

Add CSS for the toggle button and keyboard-shortcut badge

Intent: Introduce utility classes for the new discussion toggle button and the shortcut hint pill that appears inside it.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -92,6 +92,25 @@
+      .secondary-btn {
+        display: inline-flex;
+        align-items: center;
+        gap: 0.5rem;
+      }
+      .shortcut-badge {
+        display: inline-flex;
+        align-items: center;
+        justify-content: center;
+        min-width: 2.8rem;
+        padding: 0.1rem 0.4rem;
+        border-radius: 999px;
+        border: 1px solid var(--border-light);
+        background: var(--badge-bg-row);
+        color: var(--text-muted);
+        font-size: 0.74rem;
+        font-weight: 700;
+        letter-spacing: 0.04em;
+      }

Two new CSS classes are added after the existing button styles:

  • .secondary-btn – a flex container that aligns the label and shortcut badge inside the toggle button.
  • .shortcut-badge – a pill-shaped badge rendered next to the button label showing the platform-appropriate keyboard shortcut (⌘. or Ctrl+.).

These classes keep the toggle visually consistent with the existing action-row buttons while clearly surfacing the shortcut.

Create the branch-review-shell grid wrapper

Intent: Wrap the entire review area in a new CSS grid container that can expand a second column for the discussion sidebar.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -149,11 +168,24 @@
+      .branch-review-shell {
+        display: grid;
+        grid-template-columns: minmax(0, 1fr) 0;
+        gap: 1.25rem;
+        margin-top: 1rem;
+        align-items: start;
+        transition: grid-template-columns 180ms ease;
+      }
+      .branch-review-shell.discussion-open {
+        grid-template-columns: minmax(0, 1fr) clamp(320px, 24vw, 440px);
+      }
+      .branch-review-content {
+        min-width: 0;
+      }

A new .branch-review-shell element becomes the top-level grid. By default its second column is 0 wide, making the sidebar invisible. When the .discussion-open class is toggled on, the second column animates to clamp(320px, 24vw, 440px), smoothly revealing the sidebar.

The existing .review-layout loses its margin-top (now handled by the shell) and is nested inside .branch-review-content so it shares the first column with the diff section.

Style the discussion sidebar shell and its inner layout

Intent: Define the off-screen/hidden state, reveal transition, sticky positioning, and close-button styling for the sidebar discussion panel.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -203,13 +237,51 @@
+      .discussion-sidebar-shell {
+        min-width: 0;
+        opacity: 0;
+        pointer-events: none;
+        transform: translateX(1rem);
+        overflow: hidden;
+        transition: opacity 160ms ease, transform 180ms ease;
+      }
+      .branch-review-shell.discussion-open .discussion-sidebar-shell {
+        opacity: 1;
+        pointer-events: auto;
+        transform: translateX(0);
+      }
+      .discussion-sidebar-shell .discussion-panel {
+        position: sticky;
+        top: 4.4rem;
+      }
+      .discussion-sidebar-head {
+        display: flex;
+        align-items: center;
+        justify-content: space-between;
+        gap: 0.75rem;
+      }
+      .discussion-close-btn {
+        border-radius: 999px;
+        border: 1px solid var(--input-border);
+        background: var(--badge-bg-row);
+        color: var(--text-secondary);
+        padding: 0.4rem 0.75rem;
+        cursor: pointer;
+      }

The sidebar shell starts fully transparent with pointer-events: none and a slight rightward translate. When .discussion-open is applied to the parent shell, opacity/transform transition to their visible values and pointer events are re-enabled.

Inside the sidebar the .discussion-panel uses position: sticky so it follows the viewport during scroll. A new .discussion-sidebar-head flex row places the existing heading beside a round Hide button (.discussion-close-btn).

Adjust the discussion messages container for flex layout

Intent: Switch the discussion message list from a fixed max-height to a flex-growing layout so it fills the sidebar height naturally.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -287,6 +359,9 @@
+      #discussion-main {
+        display: flex;
+        flex-direction: column;
+        min-height: 0;
+        flex: 1;
+      }
       .discussion-messages {
-        min-height: 13rem;
-        max-height: 24rem;
+        min-height: 20rem;
+        max-height: none;
+        flex: 1;
         overflow-y: auto;

Previously the message list was capped at 24rem. In the sidebar context it needs to fill available vertical space, so max-height is removed and both #discussion-main and .discussion-messages become flex children with flex: 1. The discussion panel itself is given explicit min-height and height values of min(76vh, 880px) to bound the sidebar height.

Add mobile responsive overrides with fixed-position overlay and backdrop

Intent: On narrow viewports, convert the sidebar from a grid column to a full-height fixed overlay with a semi-transparent backdrop.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -338,6 +413,39 @@
+        .branch-review-shell,
+        .branch-review-shell.discussion-open {
+          display: block;
+        }
+        .discussion-sidebar-shell {
+          position: fixed;
+          top: 0;
+          right: 0;
+          bottom: 0;
+          width: min(92vw, 26rem);
+          padding: 1rem 0.75rem 1rem 0;
+          z-index: 40;
+          transform: translateX(100%);
+        }
+        .branch-review-shell.discussion-open .discussion-sidebar-shell {
+          transform: translateX(0);
+        }
+        .discussion-sidebar-shell .discussion-panel {
+          position: relative;
+          top: 0;
+          height: calc(100vh - 2rem);
+          min-height: calc(100vh - 2rem);
+        }
+        .discussion-backdrop {
+          position: fixed;
+          inset: 0;
+          background: rgba(7, 10, 18, 0.55);
+          backdrop-filter: blur(2px);
+          z-index: 30;
+        }
+        .branch-review-shell.discussion-open .discussion-backdrop {
+          display: block;
+        }

Inside the existing responsive media query:

  1. The grid shell falls back to display: block so content stacks normally.
  2. The sidebar becomes position: fixed anchored to the right edge, sliding in via translateX.
  3. A .discussion-backdrop overlay (dark semi-transparent with backdrop-filter: blur) sits behind the sidebar (z-index: 30 vs the sidebar's 40) and is shown only when .discussion-open is active.
  4. Clicking the backdrop closes the sidebar (wired in JavaScript later).

Restructure the HTML: wrap content in the shell and move discussion to an aside

Intent: Re-organise the template markup so the review content and discussion panel live as sibling grid children inside the new shell wrapper.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -375,50 +494,112 @@
-      <div class="review-layout">
+      <div id="branch-review-shell" class="branch-review-shell">
+        <div class="branch-review-content">
+          <div class="review-layout">
@@ -428,83 +609,30 @@
+        <div id="discussion-backdrop" class="discussion-backdrop" hidden></div>
+        <aside id="discussion-sidebar" class="discussion-sidebar-shell" aria-hidden="true">

The template undergoes a significant structural change:

  • A new <div id="branch-review-shell"> wraps everything.
  • Inside it, <div class="branch-review-content"> contains the existing .review-layout (file list + review main), the tutorial steps, and the diff section.
  • The discussion panel moves out of .tutorial-column and into a sibling <aside id="discussion-sidebar"> with aria-hidden="true" by default.
  • A hidden <div id="discussion-backdrop"> is placed between the content and the sidebar for mobile overlay use.
  • The discussion panel gains a header row with a Hide close button (id="discussion-close").

Add the discussion toggle button to the action row

Intent: Give users an explicit button to open/close the discussion sidebar, complete with an ARIA-expanded attribute and shortcut badge.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -365,6 +473,17 @@
+          <button
+            id="discussion-toggle"
+            type="button"
+            class="secondary-btn"
+            aria-expanded="false"
+            aria-controls="discussion-sidebar"
+            title="Toggle branch discussion"
+          >
+            <span id="discussion-toggle-label">Open Discussion</span>
+            <span id="discussion-toggle-shortcut" class="shortcut-badge">⌘.</span>
+          </button>

A new <button> is inserted into the .action-row after the CI link. It uses aria-controls to reference the sidebar's ID and starts with aria-expanded="false". The label text (Open Discussion / Hide Discussion) and the shortcut badge text are updated dynamically by JavaScript based on state and platform detection.

Wire up toggle, close, and backdrop click handlers in JavaScript

Intent: Connect the new DOM elements to the sidebar open/close logic.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -592,6 +720,13 @@
+        const reviewShell = document.getElementById('branch-review-shell');
+        const discussionToggle = document.getElementById('discussion-toggle');
+        const discussionToggleLabel = document.getElementById('discussion-toggle-label');
+        const discussionToggleShortcut = document.getElementById('discussion-toggle-shortcut');
+        const discussionSidebar = document.getElementById('discussion-sidebar');
+        const discussionBackdrop = document.getElementById('discussion-backdrop');
+        const discussionClose = document.getElementById('discussion-close');
@@ -813,6 +986,15 @@
+        if (discussionToggle) {
+          discussionToggle.addEventListener('click', () => toggleDiscussion({ focusInput: true }));
+        }
+        if (discussionClose) {
+          discussionClose.addEventListener('click', () => setDiscussionOpen(false));
+        }
+        if (discussionBackdrop) {
+          discussionBackdrop.addEventListener('click', () => setDiscussionOpen(false));
+        }

New DOM references are cached at the top of the script block. Three event listeners are registered:

  1. Toggle button click – calls toggleDiscussion({ focusInput: true }) to open or close the sidebar and auto-focus the chat input.
  2. Close button click – calls setDiscussionOpen(false) to hide the sidebar.
  3. Backdrop click – also calls setDiscussionOpen(false), dismissing the mobile overlay.

The showDiscussionMain helper is updated to use display: 'flex' instead of 'block' to match the new flex layout of #discussion-main.

Implement state management and ARIA update functions

Intent: Centralise sidebar open/close state and keep ARIA attributes, label text, and backdrop visibility in sync.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -601,6 +736,44 @@
+        let discussionOpen = false;
+
+        function updateDiscussionToggleLabel() {
+          if (discussionToggleLabel) {
+            discussionToggleLabel.textContent = discussionOpen ? 'Hide Discussion' : 'Open Discussion';
+          }
+          if (discussionToggleShortcut) {
+            discussionToggleShortcut.textContent = isMacLike ? '⌘.' : 'Ctrl+.';
+          }
+          if (discussionToggle) {
+            discussionToggle.setAttribute('aria-expanded', discussionOpen ? 'true' : 'false');
+          }
+          if (discussionSidebar) {
+            discussionSidebar.setAttribute('aria-hidden', discussionOpen ? 'false' : 'true');
+          }
+          if (discussionBackdrop) {
+            discussionBackdrop.hidden = !discussionOpen;
+          }
+        }
+
+        function setDiscussionOpen(nextOpen, options = {}) {
+          discussionOpen = !!nextOpen;
+          if (reviewShell) {
+            reviewShell.classList.toggle('discussion-open', discussionOpen);
+          }
+          updateDiscussionToggleLabel();
+          if (discussionOpen && options.focusInput && discussionInput) {
+            window.requestAnimationFrame(() => discussionInput.focus());
+          }
+        }
+
+        function toggleDiscussion(options = {}) {
+          setDiscussionOpen(!discussionOpen, options);
+        }

Three functions manage the sidebar state:

  • updateDiscussionToggleLabel() – syncs the button label, shortcut badge text, aria-expanded, aria-hidden, and backdrop hidden attribute with the current discussionOpen boolean.
  • setDiscussionOpen(nextOpen, options) – sets the boolean, toggles the .discussion-open CSS class on the shell, calls the label updater, and optionally focuses the input on the next animation frame.
  • toggleDiscussion(options) – convenience wrapper that flips the current state.

Platform detection (isMacLike) is computed once at load to display the correct modifier key in the shortcut badge.

Add keyboard shortcut (Cmd/Ctrl+.) and Escape to dismiss

Intent: Let users toggle the sidebar with a keyboard shortcut and dismiss it with Escape, skipping the shortcut when the user is typing in an input.

Affected files: crates/pika-news/templates/detail.html

Evidence
@@ -986,12 +1168,36 @@
+        window.addEventListener('keydown', (event) => {
+          const target = event.target;
+          const typingTarget = target instanceof HTMLElement && (
+            target.tagName === 'INPUT' ||
+            target.tagName === 'TEXTAREA' ||
+            target.isContentEditable
+          );
+          if (typingTarget) {
+            return;
+          }
+          const shortcutPressed =
+            event.key === '.' &&
+            ((isMacLike && event.metaKey && !event.ctrlKey) || (!isMacLike && event.ctrlKey && !event.metaKey));
+          if (shortcutPressed) {
+            event.preventDefault();
+            toggleDiscussion({ focusInput: true });
+            return;
+          }
+          if (event.key === 'Escape' && discussionOpen) {
+            setDiscussionOpen(false);
+          }
+        });

A global keydown listener is attached to window:

  1. Guard against typing targets – if the active element is an <input>, <textarea>, or contentEditable, the handler returns early so the shortcut doesn't interfere with text entry.
  2. Cmd/Ctrl + . – detected using the isMacLike flag; metaKey on Mac, ctrlKey elsewhere. Calls toggleDiscussion with focus-input behaviour.
  3. Escape – when the sidebar is open, pressing Escape closes it.

The handler calls event.preventDefault() on the shortcut to suppress any browser-default behaviour for Cmd+. / Ctrl+..

Update Rust integration test assertions

Intent: Ensure the rendered HTML contains the new structural IDs introduced by the sidebar layout.

Affected files: crates/pika-news/src/web.rs

Evidence
@@ -8310,6 +8310,9 @@
+        assert!(rendered.contains("id=\"discussion-toggle\""));
+        assert!(rendered.contains("id=\"discussion-sidebar\""));
+        assert!(rendered.contains("id=\"branch-review-shell\""));

Three new assertions are added to the existing branch-detail rendering test in web.rs:

  • id="discussion-toggle" – the toggle button exists.
  • id="discussion-sidebar" – the sidebar aside exists.
  • id="branch-review-shell" – the outer grid shell exists.

These complement the pre-existing checks for discussion content and the chat artifact ID, confirming the structural rework is present in the rendered output.

Diff