2026 03 06 Chat Ui Ux Overhaul Plan

Documentation for Plans

Chat UI/UX Overhaul Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Fix chat layout overflow, build smart tool renderers, migrate tokens, and polish all chat subcomponents to premium quality.

Architecture: ToolUseCard remains the outer shell; new ToolRendererHost dispatches to 5 category-specific renderers + 1 smart default. SmartValueDisplay and JsonTreeView are shared utilities. All components use --chat-* design tokens and container queries for responsive behavior.

Tech Stack: Angular 20+ standalone components, Signals, SCSS with CSS custom properties, Material Symbols, UxCodeBlockComponent (PrismJS), UxMarkdownComponent (marked v17).

Design Doc: docs/plans/2026-03-06-chat-ui-ux-overhaul-design.md


Phase 0: Layout Overflow Fixes (Critical, do first)

Task 1: Fix lib-unified-chat flex constraint in ai-panel-tabs

Files:

Step 1: Add flex rule for lib-unified-chat

After the existing lib-ai-panel rule (line 137-141), add:

  lib-unified-chat {
    display: flex;
    flex: 1;
    min-height: 0;
    overflow: hidden;
  }

Step 2: Verify visually

Open the app with AI panel visible. Chat should no longer clip on the left. Test in both wide (>640px) and narrow (~350px) panel widths.

Step 3: Commit

feat(session-182): fix lib-unified-chat flex constraint in ai-panel-tabs

Task 2: Fix messages container horizontal overflow

Files:

Step 1: Add overflow-x: hidden to __messages

Change line 164 from:

    overflow-y: auto;

to:

    overflow-y: auto;
    overflow-x: hidden;

Step 2: Remove min-width: 280px from __chat-area

Change line 155 from:

    min-width: 280px;

to:

    min-width: 0;

Step 3: Verify visually

Send a message with a long URL. It should wrap or truncate, never cause horizontal overflow. Test in narrow panel (320px).

Step 4: Commit

fix(session-182): prevent horizontal overflow in chat messages container

Task 3: Fix tools overflow in unified-message

Files:

Step 1: Add overflow constraint to __tools

Change the __tools block to:

  &__tools {
    margin-top: 8px;
    display: flex;
    flex-direction: column;
    gap: 4px;
    max-width: 100%;
    overflow: hidden;
  }

Step 2: Commit

fix(session-182): constrain tool cards overflow in messages

Task 4: Fix ambient bar z-index and safe-area

Files:

Step 1: Find the inline styles in the component

Search for z-index: 999999 in the component's host styles and change to z-index: 50.

Search for padding: 0 10px and change to padding: 0 max(10px, env(safe-area-inset-left)) calc(env(safe-area-inset-bottom, 0px) + 6px) max(10px, env(safe-area-inset-right)).

Search for message content rendering and add word-break: break-word to the message text style.

Step 2: Commit

fix(session-182): ambient bar z-index, safe-area, word-break

Phase 1: Design Token Foundation

Task 5: Add new design tokens to chat-variables

Files:

Step 1: Add tokens at the end of the mixin (before closing brace)

  // Mono font (Session 182)
  --chat-font-mono: 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;

  // Z-index scale (Session 182)
  --chat-z-sidebar: 30;
  --chat-z-tab-panel: 25;
  --chat-z-sidebar-backdrop: 29;
  --chat-z-dropdown: 50;
  --chat-z-ambient: 50;
  --chat-z-modal: 100;

  // Score colors for search renderers (Session 182)
  --chat-score-high: var(--chat-success);
  --chat-score-medium: var(--chat-primary);
  --chat-score-low: var(--chat-text-muted);

Step 2: Commit

feat(session-182): add mono font, z-index scale, score color tokens

Phase 2: Smart Output Utilities (shared, needed by renderers)

Task 6: Create JsonTreeView component

Files:

Step 1: Create the component

A recursive, collapsible JSON viewer. Input is a parsed JS object. Each key-value renders as a row. Objects/arrays are collapsible. Strings, numbers, booleans render as styled leaves. Uses --chat-* tokens.

Inputs:

The component references itself recursively via imports: [JsonTreeViewComponent] (Angular standalone self-import).

Step 2: Commit

feat(session-182): add JsonTreeView component for collapsible JSON display

Task 7: Create SmartValueDisplay component

Files:

Step 1: Create the component

Parses any raw string and renders it intelligently. Detection order:

  1. Try JSON.parse() -> if object/array, render via JsonTreeView
  2. Check for URL pattern (https?://) -> render as clickable link with truncation
  3. Check for file path pattern (/ prefix or common extensions) -> render with mono font + file icon
  4. Default: render as text with "Show more" toggle if > 200 chars

Inputs:

Uses --chat-* tokens, --chat-font-mono for code values.

Step 2: Commit

feat(session-182): add SmartValueDisplay for intelligent string rendering

Phase 3: Tool Renderers

Task 8: Create DefaultRenderer component

Files:

Step 1: Create the component

The smart fallback renderer. Parses output JSON into key-value pairs. Each pair rendered via SmartValueDisplay. If JSON parse fails, show full text with SmartValueDisplay.

"Show full response" toggle at bottom reveals raw JSON in UxCodeBlockComponent with language='json'.

Input:

Computed signals:

Step 2: Commit

feat(session-182): add DefaultRenderer with smart key-value display

Task 9: Create ShellRenderer component

Files:

Step 1: Create the component

For run_command and execute_code. Parses output for command, exitCode, stdout, stderr, timedOut, language.

Layout:

Input:

Step 2: Commit

feat(session-182): add ShellRenderer for command/code execution display

Task 10: Create BrowserRenderer component

Files:

Step 1: Create the component

For browser_navigate, browser_screenshot, browser_interact. Parses output for URL, title, status code, screenshot availability, action type.

Layout:

Gracefully handles truncated output (500 char limit) — if JSON parse fails, fall back to DefaultRenderer pattern.

Input:

Step 2: Commit

feat(session-182): add BrowserRenderer for web navigation display

Task 11: Create ScreenCaptureRenderer component

Files:

Step 1: Create the component

For capture_user_screen. Parses output for currentPage, viewportSize, pageContext, visibleContent, hasScreenshot.

Layout:

Input:

Step 2: Commit

feat(session-182): add ScreenCaptureRenderer for user screen context display

Task 12: Create SearchRenderer component

Files:

Step 1: Create the component

For recall_memory, search_decisions, recall_vitto_memory, query_docs, search_docs, web_search. Parses output for results[] (with varying field names: score/relevance/similarity), count, answer.

Layout:

Normalized computed signal normalizedResults() that maps different field names to a common shape: { title: string, snippet: string, score: number, url?: string }

Input:

Step 2: Commit

feat(session-182): add SearchRenderer for memory/docs/web search display

Task 13: Create DelegationRenderer component

Files:

Step 1: Create the component

For create_handoff, check_handoff_status. Parses output for status, assignedTo/toAgentType, summary, tasks[], priority, completedAt.

Layout:

Input:

Step 2: Commit

feat(session-182): add DelegationRenderer for handoff/agent display

Task 14: Create ToolRendererHost dispatcher

Files:

Step 1: Create the host component

Dispatcher that selects the right renderer based on toolCall.toolName. Uses @switch in template.

Mapping (computed signal rendererType()):

Template: @switch (rendererType()) { @case ('browser') { <lib-browser-renderer ... /> } ... }

No SCSS needed - no own styling, just passes through.

Input:

Step 2: Export all renderers from vitto-chat barrel

Add exports to libs/libs-ai/vitto-chat/src/index.ts.

Step 3: Commit

feat(session-182): add ToolRendererHost dispatcher component

Phase 4: Refactor Existing Components

Task 15: Refactor ToolUseCard (tokens + renderer integration)

Files:

Step 1: Migrate SCSS tokens

Replace all non-standard vars with --chat-* equivalents:

Add entrance animation to :host:

:host {
  display: block;
  max-width: 100%;
  overflow: hidden;
  animation: chat-scale-pop 200ms var(--chat-ease-spring);
}

Step 2: Add contextual subtitle to component

Add a subtitle computed signal that extracts a one-line summary from the tool call:

Add subtitle line to HTML below tool-name, visible without expanding.

Step 3: Replace raw JSON details with ToolRendererHost

In the HTML, replace the expanded details section (@if (expanded())) with:

@if (expanded()) {
  <div class="tool-details">
    <lib-tool-renderer-host [toolCall]="toolCall()" />
  </div>
}

Import ToolRendererHostComponent in the component's imports array.

Step 4: Add expand/collapse animation

Wrap the details in a container with CSS transition for height (use max-height trick):

.tool-details {
  animation: chat-fade-in 0.2s ease-out;
}

Step 5: Commit

feat(session-182): refactor ToolUseCard with chat tokens and renderer integration

Task 16: Refactor MessageActions (icons + animations)

Files:

Step 1: Add Material Symbols icons to each button

Replace text-only buttons with icon+label:

<button class="message-actions__btn" ...>
  <span class="material-symbols-outlined">content_copy</span>
  {{ copied() ? 'Copied!' : 'Copy' }}
</button>

Icon mapping:

Step 2: Update SCSS for icon buttons

.message-actions {
  display: flex;
  align-items: center;
  gap: 2px;
  padding-top: 4px;
  flex-wrap: wrap;

  &__btn {
    @include chat.chat-icon-button;
    width: auto;
    height: 28px;
    padding: 2px 8px;
    font-size: 0.75rem;
    border-radius: var(--chat-radius-md);
    display: inline-flex;
    align-items: center;
    gap: 4px;

    .material-symbols-outlined {
      font-size: 15px;
      font-variation-settings: 'FILL' 0, 'wght' 400, 'opsz' 20;
    }

    &--active {
      color: var(--chat-success);
      .material-symbols-outlined {
        font-variation-settings: 'FILL' 1, 'wght' 400, 'opsz' 20;
      }
    }

    &--rate-selected {
      background: var(--chat-primary-subtle);
      color: var(--chat-primary);
    }
  }
}

Step 3: Commit

feat(session-182): add icons and polish to message actions bar

Task 17: Refactor TaskProgressCard (tokens + icons)

Files:

Step 1: Replace HTML entities with Material Symbols

In HTML, replace:

Step 2: Migrate SCSS to --chat- tokens*

Replace all:

Replace @keyframes slideIn with animation: chat-scale-pop 300ms var(--chat-ease-spring) (import chat-animations).

Add gradient to phase badge and progress bar per design.

Step 3: Commit

feat(session-182): migrate TaskProgressCard to chat tokens with Material icons

Task 18: Refactor TaskFeedback (tokens)

Files:

Step 1: Migrate SCSS to --chat- tokens*

Replace all:

Step 2: Commit

feat(session-182): migrate TaskFeedback to chat tokens

Phase 5: Polish

Task 19: Polish session list items

Files:

Step 1: Enhance list item styles

Add active state:

:host(.active) .session-item {
  border-left: 2px solid var(--chat-primary);
  background: var(--chat-surface-elevated);
  padding-left: 10px; // compensate for border
}

Add hover state:

.session-item:hover {
  background: var(--chat-primary-hover);
  border-left: 2px solid color-mix(in srgb, var(--chat-primary) 30%, transparent);
  padding-left: 10px;
}

Add preview text style:

.session-preview {
  font-size: 0.75rem;
  color: var(--chat-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  margin-top: 2px;
}

Step 2: Commit

feat(session-182): polish session list item hover, active, preview states

Task 20: Polish input bar micro-interactions

Files:

Step 1: Add resize handle grip visual

.input-bar__resize-handle {
  // ... existing styles
  &::after {
    content: '';
    display: block;
    width: 32px;
    height: 3px;
    border-radius: 2px;
    background: var(--chat-border);
    margin: 0 auto;
    opacity: 0;
    transition: opacity 0.2s ease;
  }

  &:hover::after {
    opacity: 1;
  }
}

Step 2: Enhance drop zone visibility

Add drag-over state for file attachment area if applicable — dashed border, "Drop files here" text.

Step 3: Commit

feat(session-182): polish input bar resize grip and drop zone

Task 21: Polish welcome/empty state

Files:

Step 1: Enhance the welcome message

Step 2: Commit

feat(session-182): polish welcome state with breathing icon and suggestion chips

Phase 6: Export & Verify

Task 22: Update barrel exports

Files:

Step 1: Add exports for all new components

export { JsonTreeViewComponent } from './lib/components/smart-output/json-tree-view/json-tree-view.component';
export { SmartValueDisplayComponent } from './lib/components/smart-output/smart-value-display/smart-value-display.component';
export { ToolRendererHostComponent } from './lib/components/tool-renderers/tool-renderer-host/tool-renderer-host.component';
export { DefaultRendererComponent } from './lib/components/tool-renderers/default-renderer/default-renderer.component';
export { ShellRendererComponent } from './lib/components/tool-renderers/renderers/shell-renderer/shell-renderer.component';
export { BrowserRendererComponent } from './lib/components/tool-renderers/renderers/browser-renderer/browser-renderer.component';
export { ScreenCaptureRendererComponent } from './lib/components/tool-renderers/renderers/screen-capture-renderer/screen-capture-renderer.component';
export { SearchRendererComponent } from './lib/components/tool-renderers/renderers/search-renderer/search-renderer.component';
export { DelegationRendererComponent } from './lib/components/tool-renderers/renderers/delegation-renderer/delegation-renderer.component';

Step 2: Commit

feat(session-182): export all new chat components from barrel

Task 23: Visual verification pass

Step 1: Serve the app

Use the running serve process (do NOT start a new one). Navigate to the chat.

Step 2: Verify checklist

Step 3: Final commit

feat(session-182): chat UI/UX overhaul complete - Session 182

Task Dependency Graph

Phase 0 (Layout Fixes):     T1 -> T2 -> T3 -> T4  (sequential, each builds on prior)
Phase 1 (Tokens):           T5                      (independent)
Phase 2 (Utilities):        T6 -> T7                (T7 depends on T6 JsonTreeView)
Phase 3 (Renderers):        T8 -> T9, T10, T11, T12, T13 -> T14  (T8 first, then parallel, T14 last)
Phase 4 (Refactors):        T15 -> T16, T17, T18    (T15 first since it integrates renderers, rest parallel)
Phase 5 (Polish):           T19, T20, T21            (all parallel)
Phase 6 (Export/Verify):    T22 -> T23               (sequential, last)

Parallelizable groups:

Total: 23 tasks across 6 phases