# 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= ``` **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` ```javascript // 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` ```javascript // 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 ```javascript // 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:** ```css .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:** ```javascript 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:** ```javascript // 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:** ```javascript 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:** ```javascript // 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 = `
📁 ${session.workingDir}
Active Session ${session.status}
`; // Save to localStorage for persistence saveSessionState(session); } ``` #### C. Enhanced Session Status Bar **File:** `public/claude-ide/chat-enhanced.css` **Add after line 476:** ```css /* ============================================ 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):** ```html

New Chat

``` ### 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 3. ✅ **Session status bar** - Add HTML structure - Implement CSS styling - Wire up data updates 4. ✅ **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 5. ✅ **Session persistence** - Save state to localStorage - Restore on page load - Handle expiry 6. ✅ **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 7. ⚠️ **Context usage display** - Token counting - Visual progress bar - Warning when near limit 8. ⚠️ **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 ``` --- ## Appendix B: OpenCode Reference Links - **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