Files
SuperCharged-Claude-Code-Up…/docs/analysis/chat-ux-analysis.md
uroma a0fd70418f Fix multiple critical bugs: continueSessionInChat, projects link, mode buttons
Bug fixes:
- Add missing showLoadingOverlay/hideLoadingOverlay functions to ide.js
  (previously only existed in sessions-landing.js, causing continueSessionInChat to fail)
- Add loading overlay CSS styles to main style.css
- Fix Projects button URL: /projects -> /claude/ide?view=projects
- Add ?view= URL parameter handling in ide.js initialization
- Add missing Native mode button to chat view (now has 3 modes: Chat, Native, Terminal)

These fixes resolve:
1. "Continue in Chat" button not working in sessions view
2. Projects button in landing page nav taking to wrong URL
3. Missing "Native" mode button (user referred to as "Full Stack mode")
4. Loading overlay not displaying in IDE

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-21 07:03:04 +00:00

26 KiB
Raw Blame History

Chat UX Flow Analysis & Comparison with OpenCode Desktop

Date: 2026-01-20 Current URL: https://rommark.dev/claude/ide?project=L2hvbWUvdXJvbWEvb2JzaWRpYW4td2ViLWludGVyZmFjZS8ud29ya3RyZWVzL3Byb2plY3Qtb3JnYW5pemF0aW9u Reference: https://github.com/anomalyco/opencode.git


Executive Summary

The current Claude Code Web IDE has a functional but basic chat interface that lacks the polished, friction-free experience of OpenCode desktop. Key issues include:

  1. No automatic session attachment when opening with project URL parameters
  2. Manual session creation required - users must click "+ Start New Chat" before messaging
  3. Poor visual feedback for session state and attachment status
  4. Missing context indicators for working directory and project context
  5. Confusing workflow - multiple steps required to start chatting

Impact: Users opening project URLs cannot immediately start chatting, breaking the expected "open and work" flow.


1. Current State Review

1.1 Project URL Flow Analysis

URL Format:

/claude/ide?project=<base64_encoded_path>

What Works:

  • URL parameter is parsed (?project=...)
  • Base64 decoding works correctly
  • Path resolution and security checks function properly
  • Files can be served from the decoded path

What Doesn't Work:

  • No auto-session creation - Opening a project URL doesn't create a chat session
  • No immediate chat readiness - User must manually create session before typing
  • Missing context binding - Project path isn't attached to session metadata
  • No visual confirmation - User doesn't know which project is "active"

1.2 Current User Flow

1. User opens: /claude/ide?project=L2hvbWUvdXJvbWEv...
   ↓
2. IDE loads (default to dashboard view)
   ↓
3. User must click "Chat" or auto-switch to chat view
   ↓
4. User sees welcome screen with "No active sessions"
   ↓
5. User must click "+ Start New Chat"
   ↓
6. System creates session in default directory (/home/uroma/obsidian-vault)
   ↓
7. User can finally start typing

Problems:

  • 5+ steps before user can chat
  • Project path from URL is ignored
  • Session created in wrong directory
  • No context continuity

1.3 Current Implementation Analysis

File: /home/uroma/obsidian-web-interface/public/claude-ide/ide.js

// Lines 18-46: URL parameter handling
const urlParams = new URLSearchParams(window.location.search);
const sessionId = urlParams.get('session');
const prompt = urlParams.get('prompt');

if (sessionId || prompt) {
    switchView('chat');
    setTimeout(() => {
        if (sessionId) {
            attachToSession(sessionId);
        }
        if (prompt) {
            // Auto-fill and send prompt
        }
    }, 500);
}

Issues:

  • Handles ?session= parameter
  • Handles ?prompt= parameter
  • Does NOT handle ?project= parameter
  • No auto-session creation when project provided

File: /home/uroma/obsidian-web-interface/public/claude-ide/chat-functions.js

// Lines 93-144: startNewChat()
async function startNewChat() {
    resetChatState();
    clearChatDisplay();
    appendSystemMessage('Creating new chat session...');

    const res = await fetch('/claude/api/claude/sessions', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            workingDir: '/home/uroma/obsidian-vault', // ❌ HARDCODED
            metadata: { type: 'chat', source: 'web-ide' }
        })
    });
    // ...
}

Issues:

  • Working directory is hardcoded to /home/uroma/obsidian-vault
  • Doesn't check URL for project parameter
  • No way to override directory

2. OpenCode Desktop Comparison

2.1 OpenCode UX Patterns

From analyzing OpenCode's codebase and documentation:

Key Features:

  1. Session-First Architecture

    • Every interaction happens within a session context
    • Sessions are bound to working directories
    • Session state persists across UI views
  2. Seamless Project Integration

    • Opening a project immediately creates/attaches session
    • Working directory is prominent in UI
    • Context usage displayed visually
  3. Multi-Session Management

    • Tabs for switching between sessions
    • Visual indicators for active session
    • Session metadata (project, status, context)
  4. Input-First Design

    • Chat input always accessible
    • Auto-focus on load
    • No barriers to messaging
  5. Rich Context Display

    • Session context usage bar
    • LSP status indicator
    • Working directory breadcrumb
    • Provider/model selection

2.2 OpenCode Session Flow

1. Open OpenCode with project path
   ↓
2. Auto-create session with working directory
   ↓
3. Show session in tab/list
   ↓
4. Display context:
   - Working directory
   - Context usage (tokens)
   - Session status
   ↓
5. Ready for input immediately

Time to first message: ~2 seconds


3. Feature Gap Analysis

Feature Current Implementation OpenCode Desktop Priority
Auto-session on project URL Not implemented Yes HIGH
Working directory binding Hardcoded Dynamic HIGH
Session context indicator ⚠️ Basic (session ID) Rich (dir, tokens, status) MEDIUM
Visual session state ⚠️ Active/historical badges Color-coded, animated MEDIUM
Multi-session switching ⚠️ Sidebar list Tabs + sidebar LOW
Immediate chat readiness Requires manual creation Auto-created HIGH
Project metadata binding None Project name, type MEDIUM
Context usage display Not in chat view Prominent bar LOW
Terminal attachment Available Integrated LOW
File operations preview Available Rich diff view LOW

4. UX Improvements Needed

4.1 Priority 1: Friction-Free Chat Start

Problem: User must click multiple times before chatting

Solution: Auto-create session on project URL

// Enhanced URL parameter handling
const urlParams = new URLSearchParams(window.location.search);
const projectParam = urlParams.get('project');
const sessionId = urlParams.get('session');
const prompt = urlParams.get('prompt');

if (projectParam) {
    // Decode base64 project path
    const projectPath = decodeBase64(projectParam);

    // Switch to chat view
    switchView('chat');

    // Auto-create session with project directory
    autoCreateSession(projectPath);
} else if (sessionId) {
    attachToSession(sessionId);
}

Expected Flow:

User opens URL → Auto-create session → Show loading → Ready to chat
                  (2 seconds)

4.2 Priority 2: Visual Session Indicators

Problem: User doesn't know which session/project is active

Solution: Prominent session status display

UI Elements Needed:

  1. Working Directory Breadcrumb

    📁 /home/uroma/obsidian-web-interface/.worktrees/project-organization
    
  2. Session Status Badge

    ● Active Session | Running | Context: 12,450 / 200,000 tokens
    
  3. Project Metadata

    Project: project-organization | Type: Git Worktree
    

CSS Implementation:

.session-status-bar {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px 20px;
    background: #1a1a1a;
    border-bottom: 1px solid #333;
}

.working-directory {
    display: flex;
    align-items: center;
    gap: 8px;
    color: #4a9eff;
    font-size: 13px;
}

.session-indicator {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    background: rgba(81, 207, 102, 0.1);
    border: 1px solid #51cf66;
    border-radius: 4px;
    color: #51cf66;
    font-size: 12px;
}

.context-usage {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: #888;
}

4.3 Priority 3: Enhanced Feedback

Problem: Unclear what's happening during session creation

Solution: Loading states and progress indicators

States to Visualize:

  1. Creating Session (0-1s)

    ⏳ Creating session...
    
  2. Attaching to Directory (1-2s)

    📂 Attaching to /home/uroma/...
    
  3. Ready (2s+)

    ✅ Session ready! Start typing...
    

Animation:

async function autoCreateSession(projectPath) {
    // Show loading state
    showSessionCreationProgress();

    appendSystemMessage({
        type: 'progress',
        icon: '⏳',
        text: `Creating session for ${getProjectName(projectPath)}...`
    });

    // Create session
    const response = await fetch('/claude/api/claude/sessions', {
        method: 'POST',
        body: JSON.stringify({
            workingDir: projectPath,
            metadata: {
                project: getProjectName(projectPath),
                type: 'project-url',
                source: 'web-ide'
            }
        })
    });

    // Update progress
    updateProgressMessage('📂 Attached to working directory');

    // Ready state
    const data = await response.json();
    showReadyState(data.session);
}

4.4 Priority 4: Session Continuity

Problem: Losing session context when navigating

Solution: Persistent session state

Implementation:

// Save session state to localStorage
function saveSessionState(session) {
    localStorage.setItem('currentSession', JSON.stringify({
        id: session.id,
        workingDir: session.workingDir,
        metadata: session.metadata,
        timestamp: Date.now()
    }));
}

// Restore on page load
function restoreSessionState() {
    const saved = localStorage.getItem('currentSession');
    if (saved) {
        const session = JSON.parse(saved);
        // Only restore if recent (< 1 hour)
        if (Date.now() - session.timestamp < 3600000) {
            attachToSession(session.id);
        }
    }
}

5. Implementation Recommendations

5.1 Code Changes Required

A. Enhanced URL Parameter Handling

File: public/claude-ide/ide.js

Location: Lines 18-46 (DOMContentLoaded handler)

Changes:

document.addEventListener('DOMContentLoaded', () => {
    initNavigation();
    connectWebSocket();

    const urlParams = new URLSearchParams(window.location.search);
    const projectParam = urlParams.get('project');
    const sessionId = urlParams.get('session');
    const prompt = urlParams.get('prompt');

    if (projectParam) {
        // NEW: Handle project parameter
        switchView('chat');
        setTimeout(() => {
            initializeFromProjectURL(projectParam, prompt);
        }, 500);
    } else if (sessionId || prompt) {
        // Existing: Handle session/prompt parameters
        switchView('chat');
        setTimeout(() => {
            if (sessionId) {
                attachToSession(sessionId);
            }
            if (prompt) {
                setTimeout(() => {
                    const input = document.getElementById('chat-input');
                    if (input) {
                        input.value = decodeURIComponent(prompt);
                        sendChatMessage();
                    }
                }, 1000);
            }
        }, 500);
    } else {
        // Default: Check for saved session
        const savedSession = restoreSessionState();
        if (savedSession) {
            switchView('chat');
            setTimeout(() => {
                attachToSession(savedSession.id);
            }, 500);
        } else {
            switchView('chat');
        }
    }
});

B. New Session Initialization Function

File: public/claude-ide/chat-functions.js

Add after line 144:

// Initialize from Project URL
async function initializeFromProjectURL(projectParam, initialPrompt = null) {
    try {
        // Decode base64 path
        const projectPath = decodeBase64(projectParam);

        // Show loading message
        appendSystemMessage({
            type: 'loading',
            icon: '⏳',
            text: `Creating session for project...`
        });

        // Create session with project directory
        const res = await fetch('/claude/api/claude/sessions', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({
                workingDir: projectPath,
                metadata: {
                    project: extractProjectName(projectPath),
                    type: 'project-url',
                    source: 'web-ide'
                }
            })
        });

        if (!res.ok) {
            throw new Error(`Failed to create session: ${res.status}`);
        }

        const data = await res.json();

        if (data.success) {
            attachedSessionId = data.session.id;
            chatSessionId = data.session.id;

            // Update UI with project info
            updateSessionUI({
                id: data.session.id,
                workingDir: projectPath,
                project: extractProjectName(projectPath),
                status: 'running'
            });

            // Subscribe to session
            subscribeToSession(data.session.id);

            // Reload sidebar
            loadChatView();

            // Show success message
            appendSystemMessage({
                type: 'success',
                icon: '✅',
                text: `Session ready! Working in ${extractProjectName(projectPath)}`
            });

            // Auto-send initial prompt if provided
            if (initialPrompt) {
                setTimeout(() => {
                    const input = document.getElementById('chat-input');
                    if (input) {
                        input.value = decodeURIComponent(initialPrompt);
                        sendChatMessage();
                    }
                }, 1000);
            }
        }
    } catch (error) {
        console.error('Error initializing from project URL:', error);
        appendSystemMessage({
            type: 'error',
            icon: '❌',
            text: `Failed to create session: ${error.message}`
        });
    }
}

// Decode base64 string
function decodeBase64(str) {
    try {
        return Buffer.from(str, 'base64').toString('utf-8');
    } catch (error) {
        console.error('Base64 decode error:', error);
        throw new Error('Invalid project path encoding');
    }
}

// Extract project name from path
function extractProjectName(path) {
    const parts = path.split('/');
    return parts[parts.length - 1] || 'Untitled Project';
}

// Update session UI with context
function updateSessionUI(session) {
    // Update session ID
    document.getElementById('current-session-id').textContent = session.id;

    // Update title
    document.getElementById('chat-title').textContent = session.project || 'New Chat';

    // Add or update session status bar
    let statusBar = document.querySelector('.session-status-bar');
    if (!statusBar) {
        statusBar = document.createElement('div');
        statusBar.className = 'session-status-bar';
        document.getElementById('chat-header').after(statusBar);
    }

    statusBar.innerHTML = `
        <div class="working-directory">
            <span>📁</span>
            <span>${session.workingDir}</span>
        </div>
        <div class="session-indicator">
            <span class="status-dot"></span>
            <span>Active Session</span>
            <span>•</span>
            <span>${session.status}</span>
        </div>
    `;

    // Save to localStorage for persistence
    saveSessionState(session);
}

C. Enhanced Session Status Bar

File: public/claude-ide/chat-enhanced.css

Add after line 476:

/* ============================================
   Session Status Bar
   ============================================ */

.session-status-bar {
    display: flex;
    align-items: center;
    gap: 16px;
    padding: 12px 20px;
    background: #1a1a1a;
    border-bottom: 1px solid #333;
    flex-wrap: wrap;
    animation: slideDown 0.3s ease;
}

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

.working-directory {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 12px;
    background: rgba(74, 158, 255, 0.1);
    border: 1px solid #4a9eff;
    border-radius: 6px;
    color: #4a9eff;
    font-size: 13px;
    font-weight: 500;
}

.working-directory span:first-child {
    font-size: 16px;
}

.session-indicator {
    display: flex;
    align-items: center;
    gap: 6px;
    padding: 4px 12px;
    background: rgba(81, 207, 102, 0.1);
    border: 1px solid #51cf66;
    border-radius: 4px;
    color: #51cf66;
    font-size: 12px;
    font-weight: 500;
}

.status-dot {
    width: 8px;
    height: 8px;
    background: #51cf66;
    border-radius: 50%;
    animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
    0%, 100% {
        opacity: 1;
    }
    50% {
        opacity: 0.5;
    }
}

.context-usage {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12px;
    color: #888;
    margin-left: auto;
}

.context-usage-bar {
    width: 100px;
    height: 4px;
    background: #333;
    border-radius: 2px;
    overflow: hidden;
}

.context-usage-fill {
    height: 100%;
    background: linear-gradient(90deg, #4a9eff, #51cf66);
    transition: width 0.3s ease;
}

/* ============================================
   System Message Enhancements
   ============================================ */

.chat-message.system {
    display: flex;
    justify-content: center;
    padding: 12px 20px;
    margin: 8px 0;
}

.chat-message.system .chat-message-bubble {
    background: #1a1a1a;
    border: 1px solid #333;
    border-radius: 8px;
    padding: 10px 16px;
    font-size: 13px;
    display: flex;
    align-items: center;
    gap: 8px;
}

.chat-message.system.loading .chat-message-bubble {
    border-color: #ffa94d;
    color: #ffa94d;
}

.chat-message.system.success .chat-message-bubble {
    border-color: #51cf66;
    color: #51cf66;
}

.chat-message.system.error .chat-message-bubble {
    border-color: #ff6b6b;
    color: #ff6b6b;
}

/* ============================================
   Session List Enhancements
   ============================================ */

.chat-history-item {
    position: relative;
    transition: all 0.2s ease;
}

.chat-history-item.active::before {
    content: '';
    position: absolute;
    left: 0;
    top: 50%;
    transform: translateY(-50%);
    width: 3px;
    height: 60%;
    background: #4a9eff;
    border-radius: 0 3px 3px 0;
    animation: slideIn 0.3s ease;
}

@keyframes slideIn {
    from {
        height: 0;
    }
    to {
        height: 60%;
    }
}

.chat-history-item .working-dir-hint {
    font-size: 11px;
    color: #888;
    margin-top: 4px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

D. HTML Structure Updates

File: public/claude-ide/index.html

Update chat header (lines 130-139):

<div class="chat-header" id="chat-header">
    <div class="chat-session-info">
        <h2 id="chat-title">New Chat</h2>
        <span class="chat-session-id" id="current-session-id"></span>
    </div>
    <div class="chat-actions">
        <button class="btn-secondary btn-sm" onclick="showSessionInfo()" title="Session Info"></button>
        <button class="btn-secondary btn-sm" onclick="clearChat()" title="Clear chat">Clear</button>
        <button class="btn-secondary btn-sm" onclick="showChatSettings()" title="Settings">⚙️</button>
    </div>
</div>

<!-- Session status bar (inserted dynamically) -->

5.2 Backend Changes (if needed)

File: server.js

No changes required - existing session creation endpoint already supports:

  • Custom workingDir parameter
  • metadata object for project info
  • Session management

6. Implementation Priority List

Phase 1: Critical (Week 1)

Goal: Make project URLs work immediately

  1. Auto-session creation on project URL

    • Implement initializeFromProjectURL()
    • Update URL parameter handling
    • Test with sample project URLs
  2. Working directory binding

    • Pass project path to session creation
    • Store in session metadata
    • Display in UI

Estimated time: 4-6 hours

Phase 2: Enhanced UX (Week 1-2)

Goal: Improve visual feedback

  1. Session status bar

    • Add HTML structure
    • Implement CSS styling
    • Wire up data updates
  2. Loading states

    • Show creation progress
    • Visual indicators for states
    • Error handling messages

Estimated time: 3-4 hours

Phase 3: Polish (Week 2)

Goal: Match OpenCode experience

  1. Session persistence

    • Save state to localStorage
    • Restore on page load
    • Handle expiry
  2. Enhanced system messages

    • Styled loading/success/error states
    • Better icons and animations
    • Clear action feedback

Estimated time: 2-3 hours

Phase 4: Nice-to-Have (Week 3+)

Goal: Exceed expectations

  1. ⚠️ Context usage display

    • Token counting
    • Visual progress bar
    • Warning when near limit
  2. ⚠️ Quick project switcher

    • Dropdown for recent projects
    • Keyboard shortcuts
    • Project history

Estimated time: 4-6 hours


7. Testing Checklist

Manual Testing

  • Project URL Flow

    • Open IDE with ?project= parameter
    • Verify session auto-created
    • Check working directory is correct
    • Confirm project metadata saved
  • Session Persistence

    • Create session
    • Refresh page
    • Verify session restored
    • Check localStorage
  • Visual Feedback

    • Status bar displays correctly
    • Loading animations work
    • Error messages show properly
    • Success indicators visible
  • Multi-Session

    • Create multiple sessions
    • Switch between them
    • Verify each maintains state
    • Check sidebar updates

Edge Cases

  • Invalid base64 in project URL
  • Non-existent project path
  • Path outside allowed directories
  • Session creation failure
  • WebSocket connection issues
  • Rapid page refreshes

8. Success Metrics

Quantitative

  • Time to First Message

    • Current: ~30 seconds (manual setup)
    • Target: < 5 seconds (auto-create)
    • Measurement: Page load to first valid send
  • Steps to Chat

    • Current: 5+ steps
    • Target: 1 step (open URL)
    • Measurement: User actions required
  • Error Rate

    • Current: Unknown
    • Target: < 5% failed session creations
    • Measurement: Failed / total attempts

Qualitative

  • User Feedback

    • "I can start working immediately"
    • "I know which project I'm working in"
    • "The interface feels responsive"
  • Comparison to OpenCode

    • Feature parity on core flows
    • Competitive visual polish
    • Unique web advantages (URLs, sharing)

9. Open Questions

  1. Session Expiry

    • How long should sessions persist?
    • Should we auto-expire inactive sessions?
    • Recommendation: 1 hour for persistence, 24 hours for session files
  2. Project URL Format

    • Is base64 encoding the best approach?
    • Should we support short codes/slugs?
    • Recommendation: Keep base64, add slug option for sharing
  3. Multi-Session Limits

    • How many concurrent sessions?
    • Memory management?
    • Recommendation: Max 5 active sessions, auto-close oldest
  4. Context Usage

    • Should we show real-time token usage?
    • How to count tokens accurately?
    • Recommendation: Add in Phase 4, estimate initially

10. Next Steps

  1. Review this document with team
  2. Prioritize phases based on resources
  3. Create development branch for chat UX improvements
  4. Implement Phase 1 (auto-session creation)
  5. Test with real projects
  6. Iterate based on feedback

Appendix A: Example URLs

Current URL

https://rommark.dev/claude/ide?project=L2hvbWUvdXJvbWEvb2JzaWRpYW4td2ViLWludGVyZmFjZS8ud29ya3RyZWVzL3Byb2plY3Qtb3JnYW5pemF0aW9u

Decoded Path

/home/uroma/obsidian-web-interface/.worktrees/project-organization

Future URL with Prompt

https://rommark.dev/claude/ide?project=L2hvbWUvdXJvbWEv...&prompt=Explain%20the%20architecture

Future URL with Session

https://rommark.dev/claude/ide?session=session-abc123&prompt=Continue%20working

  • Repository: https://github.com/anomalyco/opencode
  • Session Management: /tmp/opencode/packages/app/src/pages/session.tsx
  • Session Components: /tmp/opencode/packages/app/src/components/session/
  • UI Components: /tmp/opencode/packages/ui/src/components/

Document Version: 1.0 Last Updated: 2026-01-20 Author: Claude (Sonnet 4.5) Status: Ready for Review