# NexusCLI Chat UI Design

**Version**: 0.1.0
**Created**: 2025-11-17
**Inspired by**: LibreChat UI + ChatGPT
**Focus**: CLI job execution with real-time status

---

## 🎨 Design Philosophy

**LibreChat Style + CLI-Specific Features**

```
┌────────────────────────────────────────────────────────────┐
│  LibreChat Base                  CLI Enhancements          │
│  ────────────────                ─────────────────         │
│  • Chat bubbles                  • Job status line         │
│  • Markdown rendering            • Real-time progress      │
│  • Dark theme                    • Exit code badges        │
│  • Streaming text                • Command preview         │
│  • Code blocks                   • Collapsible output      │
└────────────────────────────────────────────────────────────┘
```

---

## 📐 Layout Structure

### Main Layout (LibreChat-inspired)

```
┌─────────────────────────────────────────────────────────────┐
│  ┌──────────────┐  ┌──────────────────────────────────────┐│
│  │              │  │  NexusCLI           [@] [⚙️] [user]  ││
│  │ Conversations│  ├──────────────────────────────────────┤│
│  │  Sidebar     │  │                                      ││
│  │              │  │  ┌────────────────────────────────┐ ││
│  │ + New Chat   │  │  │ 🤖 System                      │ ││
│  │              │  │  │ 3 nodes online. Ready for      │ ││
│  │ Today        │  │  │ commands.                      │ ││
│  │ • Deploy...  │  │  └────────────────────────────────┘ ││
│  │ • Check logs │  │                                      ││
│  │              │  │  ┌────────────────────────────────┐ ││
│  │ Yesterday    │  │  │ 👤 You                         │ ││
│  │ • Fix nginx  │  │  │ systemctl status nginx         │ ││
│  │              │  │  └────────────────────────────────┘ ││
│  │ Last Week    │  │                                      ││
│  │ • Update...  │  │  ┌────────────────────────────────┐ ││
│  │              │  │  │ 🤖 NexusCLI                    │ ││
│  │              │  │  │ ▶ Executing on prod-001...     │ ││
│  │              │  │  │   🔧 bash: systemctl status... │ ││
│  │              │  │  │                                │ ││
│  │              │  │  │ ```                            │ ││
│  │              │  │  │ ● nginx.service - running      │ ││
│  │              │  │  │   Active: active (running)     │ ││
│  │              │  │  │ ```                            │ ││
│  │              │  │  │ ✅ Exit code: 0 (1.2s)         │ ││
│  │              │  │  └────────────────────────────────┘ ││
│  │              │  │                                      ││
│  │              │  │  ┌────────────────────────────────┐ ││
│  │              │  │  │ Type command or question...    │ ││
│  │              │  │  │                          [↑]   │ ││
│  │              │  │  └────────────────────────────────┘ ││
│  └──────────────┘  └──────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
```

---

## 💬 Message Types

### 1. User Message (Command Input)

**Visual**:
```
┌────────────────────────────────────────────┐
│ 👤 You                            10:30 AM │
│ systemctl status nginx                    │
└────────────────────────────────────────────┘
```

**Code**:
```jsx
<div className="message user-message">
  <div className="message-header">
    <span className="avatar">👤</span>
    <span className="sender">You</span>
    <span className="timestamp">10:30 AM</span>
  </div>
  <div className="message-content">
    <code>systemctl status nginx</code>
  </div>
</div>
```

---

### 2. Assistant Message (Job Execution)

#### a) **Queued State**

```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ ⏱️  Queued on prod-001...                  │
└────────────────────────────────────────────┘
```

#### b) **Executing State** (Real-time Status)

```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ ▶ Executing on prod-001...                │
│   🔧 bash: systemctl status nginx          │
│                                            │
│ ● nginx.service - A high performance...   │
│    Loaded: loaded (/lib/systemd/system... │
│    Active: active (running) since...      │ ← Streaming output
└────────────────────────────────────────────┘
```

**Key Feature**: Status line updates in real-time
- `🔧 bash: systemctl status nginx` ← Shows what CLI is doing
- Updates as new events arrive via SSE

#### c) **Completed State**

```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ ✅ Completed on prod-001 (exit: 0, 1.2s)  │
│                                            │
│ ```bash                                    │
│ ● nginx.service - A high performance...   │
│    Loaded: loaded (/lib/systemd/system... │
│    Active: active (running) since Mon...  │
│    Main PID: 1234 (nginx)                 │
│ ```                                        │
│                                            │
│ [📋 Copy] [↻ Re-run] [⋯ More]             │
└────────────────────────────────────────────┘
```

**Features**:
- ✅ Success badge with exit code and duration
- Code block with syntax highlighting
- Action buttons (Copy, Re-run, More)

#### d) **Failed State**

```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ ❌ Failed on prod-003 (exit: 3, 0.5s)     │
│                                            │
│ ```bash                                    │
│ ● nginx.service - inactive (dead)         │
│ ```                                        │
│                                            │
│ stderr:                                    │
│ ```                                        │
│ Failed to start nginx.service: Unit...    │
│ ```                                        │
│                                            │
│ [🔍 Debug] [↻ Retry] [⋯ More]             │
└────────────────────────────────────────────┘
```

---

### 3. Multi-Node Job (Batch Execution)

```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ Executing on 3 nodes...                   │
│                                            │
│ [prod-001] ✅ nginx active (0.8s)          │
│ [prod-002] ✅ nginx active (1.1s)          │
│ [prod-003] ⚠️  nginx inactive (0.5s)       │
│                                            │
│ 2/3 nodes healthy                          │
│                                            │
│ [📊 Details] [↻ Re-run Failed]            │
└────────────────────────────────────────────┘
```

**Expandable Details**:
```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ Executing on 3 nodes...           [▼]     │
│                                            │
│ ┌─ prod-001 ─────────────────────────┐    │
│ │ ✅ Exit: 0 (0.8s)                  │    │
│ │ ● nginx.service - running          │    │
│ └────────────────────────────────────┘    │
│                                            │
│ ┌─ prod-002 ─────────────────────────┐    │
│ │ ✅ Exit: 0 (1.1s)                  │    │
│ │ ● nginx.service - running          │    │
│ └────────────────────────────────────┘    │
│                                            │
│ ┌─ prod-003 ─────────────────────────┐    │
│ │ ❌ Exit: 3 (0.5s)                  │    │
│ │ ● nginx.service - inactive (dead)  │    │
│ └────────────────────────────────────┘    │
└────────────────────────────────────────────┘
```

---

## 🎭 Status Indicators

### Icon Library (CLI-specific)

| State | Icon | Color | Meaning |
|-------|------|-------|---------|
| **Queued** | ⏱️ | Gray | Job waiting in queue |
| **Executing** | ▶ | Blue | Job currently running |
| **Tool execution** | 🔧 | Blue | Specific tool running |
| **Success** | ✅ | Green | Job completed successfully |
| **Failed** | ❌ | Red | Job failed (exit != 0) |
| **Warning** | ⚠️ | Yellow | Job completed with warnings |
| **Cancelled** | ⛔ | Gray | Job cancelled by user |
| **Streaming** | 💬 | Blue | Assistant streaming response |
| **Thinking** | 🤔 | Purple | Processing request |

### Status Line Format

**Pattern**: `[Icon] [Action] on [Node]... [Detail]`

**Examples**:
```
⏱️  Queued on prod-001...
▶ Executing on prod-001...
  🔧 bash: systemctl status nginx
  🔧 bash: journalctl -n 50
✅ Completed on prod-001 (exit: 0, 1.2s)
❌ Failed on prod-003 (exit: 3, 0.5s)
⚠️  Completed with warnings (exit: 0, 2.1s)
```

---

## 🎨 Styling (LibreChat-inspired)

### Color Palette

```css
:root {
  /* Dark theme (default) */
  --bg-primary: #0d0d0d;           /* Main background */
  --bg-secondary: #1a1a1a;         /* Sidebar, cards */
  --bg-tertiary: #2a2a2a;          /* User message bg */

  --text-primary: #ececec;         /* Main text */
  --text-secondary: #9b9b9b;       /* Muted text */
  --text-tertiary: #6e6e6e;        /* Very muted */

  --accent-primary: #10a37f;       /* Success, primary actions */
  --accent-secondary: #667eea;     /* Links, highlights */

  --border-color: #303030;         /* Borders, dividers */

  /* Status colors */
  --status-success: #4ade80;       /* Green */
  --status-error: #ef4444;         /* Red */
  --status-warning: #fbbf24;       /* Yellow */
  --status-info: #3b82f6;          /* Blue */

  /* Code block */
  --code-bg: #1e1e1e;
  --code-text: #d4d4d4;
}
```

### Message Styling

```css
/* Message container */
.message {
  display: flex;
  flex-direction: column;
  padding: 1.5rem;
  margin-bottom: 1rem;
  border-radius: 8px;
  max-width: 100%;
}

/* User message */
.user-message {
  background: var(--bg-tertiary);
  align-self: flex-end;
  max-width: 80%;
}

.user-message .message-content {
  font-family: 'Fira Code', monospace;
  color: var(--text-primary);
}

/* Assistant message */
.assistant-message {
  background: var(--bg-secondary);
  border: 1px solid var(--border-color);
  max-width: 100%;
}

/* Message header */
.message-header {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  font-size: 0.875rem;
  color: var(--text-secondary);
}

.avatar {
  font-size: 1.25rem;
}

.sender {
  font-weight: 600;
  color: var(--text-primary);
}

.timestamp {
  margin-left: auto;
  font-size: 0.75rem;
}

/* Status line */
.status-line {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  background: rgba(59, 130, 246, 0.1);
  border-left: 3px solid var(--status-info);
  border-radius: 4px;
  font-size: 0.875rem;
  margin-bottom: 0.75rem;
}

.status-line.success {
  background: rgba(74, 222, 128, 0.1);
  border-color: var(--status-success);
}

.status-line.error {
  background: rgba(239, 68, 68, 0.1);
  border-color: var(--status-error);
}

.status-line.warning {
  background: rgba(251, 191, 36, 0.1);
  border-color: var(--status-warning);
}

/* Code block */
.code-block {
  background: var(--code-bg);
  border-radius: 6px;
  padding: 1rem;
  overflow-x: auto;
  margin: 0.5rem 0;
  font-family: 'Fira Code', 'Consolas', monospace;
  font-size: 0.875rem;
  line-height: 1.5;
}

.code-block pre {
  margin: 0;
  color: var(--code-text);
}

/* Action buttons */
.message-actions {
  display: flex;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.action-btn {
  padding: 0.375rem 0.75rem;
  background: transparent;
  border: 1px solid var(--border-color);
  border-radius: 4px;
  color: var(--text-secondary);
  font-size: 0.75rem;
  cursor: pointer;
  transition: all 0.2s;
}

.action-btn:hover {
  background: var(--bg-tertiary);
  color: var(--text-primary);
}
```

---

## ⚛️ React Components

### 1. Message Component

```jsx
// components/Message.jsx
import React, { useState } from 'react';
import ReactMarkdown from 'react-markdown';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { vscDarkPlus } from 'react-syntax-highlighter/dist/esm/styles/prism';

function Message({ message, streaming = false }) {
  const [expanded, setExpanded] = useState(false);

  const { role, content, status, metadata } = message;

  return (
    <div className={`message ${role}-message`}>
      <div className="message-header">
        <span className="avatar">{role === 'user' ? '👤' : '🤖'}</span>
        <span className="sender">{role === 'user' ? 'You' : 'NexusCLI'}</span>
        <span className="timestamp">
          {new Date(message.created_at).toLocaleTimeString()}
        </span>
      </div>

      {/* Status line (for assistant messages with jobs) */}
      {role === 'assistant' && status && (
        <StatusLine status={status} metadata={metadata} />
      )}

      {/* Message content */}
      <div className="message-content">
        <ReactMarkdown
          components={{
            code({ node, inline, className, children, ...props }) {
              const match = /language-(\w+)/.exec(className || '');
              return !inline && match ? (
                <SyntaxHighlighter
                  style={vscDarkPlus}
                  language={match[1]}
                  PreTag="div"
                  {...props}
                >
                  {String(children).replace(/\n$/, '')}
                </SyntaxHighlighter>
              ) : (
                <code className={className} {...props}>
                  {children}
                </code>
              );
            }
          }}
        >
          {content}
        </ReactMarkdown>
      </div>

      {/* Streaming indicator */}
      {streaming && (
        <div className="streaming-indicator">
          <span className="dot"></span>
          <span className="dot"></span>
          <span className="dot"></span>
        </div>
      )}

      {/* Action buttons (for completed jobs) */}
      {role === 'assistant' && status?.state === 'completed' && (
        <MessageActions message={message} />
      )}
    </div>
  );
}

export default Message;
```

---

### 2. StatusLine Component

```jsx
// components/StatusLine.jsx
import React from 'react';

const STATUS_CONFIG = {
  queued: {
    icon: '⏱️',
    label: 'Queued',
    className: 'info'
  },
  executing: {
    icon: '▶',
    label: 'Executing',
    className: 'info'
  },
  completed: {
    icon: '✅',
    label: 'Completed',
    className: 'success'
  },
  failed: {
    icon: '❌',
    label: 'Failed',
    className: 'error'
  },
  cancelled: {
    icon: '⛔',
    label: 'Cancelled',
    className: 'warning'
  }
};

function StatusLine({ status, metadata }) {
  const config = STATUS_CONFIG[status.state] || STATUS_CONFIG.executing;

  // Build status message
  let message = '';

  if (status.state === 'queued') {
    message = `Queued on ${status.nodeId}...`;
  } else if (status.state === 'executing') {
    message = `Executing on ${status.nodeId}...`;

    // Add tool execution detail
    if (status.currentTool) {
      message += `\n  🔧 ${status.currentTool}: ${status.command}`;
    }
  } else if (status.state === 'completed') {
    const duration = ((metadata?.duration || 0) / 1000).toFixed(1);
    message = `Completed on ${status.nodeId} (exit: ${metadata?.exitCode || 0}, ${duration}s)`;
  } else if (status.state === 'failed') {
    const duration = ((metadata?.duration || 0) / 1000).toFixed(1);
    message = `Failed on ${status.nodeId} (exit: ${metadata?.exitCode || 1}, ${duration}s)`;
  }

  return (
    <div className={`status-line ${config.className}`}>
      <span className="status-icon">{config.icon}</span>
      <span className="status-message">{message}</span>
    </div>
  );
}

export default StatusLine;
```

---

### 3. MessageActions Component

```jsx
// components/MessageActions.jsx
import React from 'react';

function MessageActions({ message }) {
  const handleCopy = () => {
    navigator.clipboard.writeText(message.content);
    // Show toast notification
  };

  const handleRerun = () => {
    // Re-execute the job
    console.log('Re-run job:', message.metadata?.jobId);
  };

  const handleDetails = () => {
    // Show job details modal
    console.log('Show details:', message.metadata?.jobId);
  };

  return (
    <div className="message-actions">
      <button className="action-btn" onClick={handleCopy}>
        📋 Copy
      </button>
      <button className="action-btn" onClick={handleRerun}>
        ↻ Re-run
      </button>
      <button className="action-btn" onClick={handleDetails}>
        ⋯ More
      </button>
    </div>
  );
}

export default MessageActions;
```

---

## 🌊 SSE Integration (Real-time Updates)

### Frontend EventSource Handler

```jsx
// hooks/useJobStream.js
import { useState, useEffect } from 'react';

export function useJobStream(jobId) {
  const [status, setStatus] = useState({ state: 'queued' });
  const [output, setOutput] = useState('');
  const [completed, setCompleted] = useState(false);

  useEffect(() => {
    if (!jobId) return;

    const eventSource = new EventSource(`/api/v1/jobs/${jobId}/stream`);

    eventSource.onmessage = (event) => {
      const data = JSON.parse(event.data);

      if (data.type === 'status') {
        // Update status line
        setStatus({
          state: data.category,
          nodeId: data.nodeId,
          currentTool: data.tool,
          command: data.message,
        });
      }

      if (data.type === 'output_chunk') {
        // Append to output (streaming)
        setOutput(prev => prev + data.text);
      }

      if (data.type === 'done') {
        // Mark as completed
        setCompleted(true);
        setStatus(prev => ({
          ...prev,
          state: data.exitCode === 0 ? 'completed' : 'failed'
        }));
        eventSource.close();
      }
    };

    eventSource.onerror = () => {
      eventSource.close();
    };

    return () => {
      eventSource.close();
    };
  }, [jobId]);

  return { status, output, completed };
}
```

### Usage in Message Component

```jsx
// In assistant message rendering
function AssistantMessage({ message }) {
  const jobId = message.metadata?.jobId;
  const { status, output, completed } = useJobStream(jobId);

  return (
    <div className="assistant-message">
      <StatusLine status={status} metadata={message.metadata} />

      {/* Show streaming output */}
      {output && (
        <pre className="code-block">{output}</pre>
      )}

      {/* Show final message when completed */}
      {completed && message.content && (
        <ReactMarkdown>{message.content}</ReactMarkdown>
      )}
    </div>
  );
}
```

---

## 📱 Mobile Responsive

### Breakpoints

```css
/* Mobile (Termux on phone) */
@media (max-width: 768px) {
  .chat-layout {
    flex-direction: column;
  }

  .sidebar {
    position: fixed;
    left: -300px;
    transition: left 0.3s;
    z-index: 100;
  }

  .sidebar.open {
    left: 0;
  }

  .message {
    padding: 1rem;
    font-size: 14px;
  }

  .user-message {
    max-width: 95%;
  }

  .code-block {
    font-size: 12px;
    overflow-x: scroll;
  }
}

/* Tablet */
@media (min-width: 769px) and (max-width: 1024px) {
  .sidebar {
    width: 200px;
  }
}

/* Desktop */
@media (min-width: 1025px) {
  .sidebar {
    width: 260px;
  }

  .chat-container {
    max-width: 900px;
    margin: 0 auto;
  }
}
```

---

## 🎯 Example: Complete Job Flow

### 1. User sends command

```
┌────────────────────────────────────────────┐
│ 👤 You                            10:30 AM │
│ systemctl restart nginx                   │
└────────────────────────────────────────────┘
```

### 2. Assistant queues job

```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ ⏱️  Queued on prod-001...                  │
└────────────────────────────────────────────┘
```

### 3. Job starts executing (status updates)

```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ ▶ Executing on prod-001...                │
│   🔧 bash: systemctl restart nginx         │
└────────────────────────────────────────────┘
```

### 4. Output streams in

```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ ▶ Executing on prod-001...                │
│   🔧 bash: systemctl restart nginx         │
│                                            │
│ Stopping nginx.service...                 │ ← Streaming
│ Starting nginx.service...                 │
│ Started nginx.service                     │
└────────────────────────────────────────────┘
```

### 5. Job completes (final state)

```
┌────────────────────────────────────────────┐
│ 🤖 NexusCLI                       10:30 AM │
│ ✅ Completed on prod-001 (exit: 0, 2.1s)  │
│                                            │
│ ```bash                                    │
│ Stopping nginx.service...                 │
│ Starting nginx.service...                 │
│ Started nginx.service                     │
│ ```                                        │
│                                            │
│ nginx restarted successfully.              │
│                                            │
│ [📋 Copy] [↻ Re-run] [⋯ More]             │
└────────────────────────────────────────────┘
```

---

## 🎨 Animation & UX Polish

### Streaming Dots Animation

```css
@keyframes blink {
  0%, 100% { opacity: 0.2; }
  50% { opacity: 1; }
}

.streaming-indicator {
  display: flex;
  gap: 0.25rem;
  align-items: center;
  margin-top: 0.5rem;
}

.streaming-indicator .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--accent-secondary);
  animation: blink 1.4s infinite;
}

.streaming-indicator .dot:nth-child(2) {
  animation-delay: 0.2s;
}

.streaming-indicator .dot:nth-child(3) {
  animation-delay: 0.4s;
}
```

### Status Line Pulse

```css
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.7; }
}

.status-line.executing .status-icon {
  animation: pulse 2s infinite;
}
```

### Smooth Transitions

```css
.message {
  animation: slideIn 0.3s ease-out;
}

@keyframes slideIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}
```

---

## 📚 Related Documents

- [Design Principles](./DESIGN_PRINCIPLES.md) - Three pillars
- [Database Schema](./DATABASE_SCHEMA.md) - Chat/jobs storage
- [API Contract](./API_WRAPPER_CONTRACT.md) - SSE events spec

---

## 🚀 Implementation Checklist

### Phase 1: Basic Chat UI
- [ ] Message component (user + assistant)
- [ ] Markdown rendering
- [ ] Code block syntax highlighting
- [ ] Conversation sidebar

### Phase 2: Job Status
- [ ] StatusLine component
- [ ] Real-time SSE integration
- [ ] Status updates (queued → executing → done)
- [ ] Tool execution indicator

### Phase 3: Output Streaming
- [ ] Incremental output rendering
- [ ] Code block formatting
- [ ] Collapsible long output
- [ ] Copy to clipboard

### Phase 4: Actions & UX
- [ ] Re-run button
- [ ] Job details modal
- [ ] Multi-node batch display
- [ ] Mobile responsive layout

---

_UI Design for NexusCLI - LibreChat Style + CLI Enhancements_
_Generated by Claude Code (Sonnet 4.5) - 2025-11-17_
