Initial commit: Obsidian Web Interface for Claude Code
- Full IDE with terminal integration using xterm.js - Session management with local and web sessions - HTML preview functionality - Multi-terminal support with session picker Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
1502
docs/plans/2025-01-19-landing-page-implementation.md
Normal file
1502
docs/plans/2025-01-19-landing-page-implementation.md
Normal file
File diff suppressed because it is too large
Load Diff
247
docs/plans/2025-01-19-landing-page-workflow-design.md
Normal file
247
docs/plans/2025-01-19-landing-page-workflow-design.md
Normal file
@@ -0,0 +1,247 @@
|
||||
# Landing Page Workflow Design
|
||||
|
||||
**Date:** 2025-01-19
|
||||
**Status:** Approved
|
||||
**Author:** Claude (with user collaboration)
|
||||
|
||||
## Overview
|
||||
|
||||
Enhance the `/claude/` landing page to provide a streamlined "Create New Project" or "Load Existing Project" workflow with improved UX while maintaining fast navigation to the IDE.
|
||||
|
||||
**Design Philosophy:** Speed and simplicity - minimize friction for developers who want to get from idea → code as quickly as possible.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Component Structure
|
||||
|
||||
```
|
||||
SessionsPage
|
||||
├── SessionsGrid (main container)
|
||||
│ └── SessionCard (enhanced with metadata)
|
||||
├── QuickStartGrid
|
||||
│ ├── TemplateCard (React, Node.js, etc.)
|
||||
│ └── BlankProjectCard (with inline name input)
|
||||
└── NavigationManager (handles transitions)
|
||||
```
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
Page Load → Fetch sessions → Cache locally → Render grid
|
||||
↓
|
||||
User clicks card → Show loading overlay → Navigate to /claude/ide
|
||||
```
|
||||
|
||||
### State Management
|
||||
|
||||
```javascript
|
||||
{
|
||||
sessions: Map<sessionId, sessionData>,
|
||||
loading: boolean,
|
||||
error: string | null,
|
||||
isNavigating: boolean
|
||||
}
|
||||
```
|
||||
|
||||
## UI/UX Design
|
||||
|
||||
### Enhanced Session Cards
|
||||
|
||||
**Layout:**
|
||||
- **Left**: Icon (💬 active, 📁 historical)
|
||||
- **Middle**:
|
||||
- Project name (bold, 20px) - **inline editable**
|
||||
- Working directory (monospace, 13px, gray)
|
||||
- Last message preview (14px, truncated to 100 chars)
|
||||
- File count badge (📄 N files)
|
||||
- Relative time ("5 min ago")
|
||||
- **Right**:
|
||||
- **Continue** button (primary CTA)
|
||||
- ⋮ menu (Duplicate, Delete)
|
||||
|
||||
**Interactions:**
|
||||
- Hover: Lift effect (translateY: -4px), border highlight
|
||||
- Click project name → inline edit mode
|
||||
- Click Continue → loading overlay → navigate to IDE
|
||||
|
||||
### Inline Name Editing
|
||||
|
||||
- Click name → transforms to input field
|
||||
- Save on blur or Enter
|
||||
- Revert on Escape
|
||||
- Show spinner while saving
|
||||
- Validation: Max 50 chars, no special characters
|
||||
|
||||
### Blank Project Card
|
||||
|
||||
- Shows input field instead of static text
|
||||
- Placeholder: "Enter project name..."
|
||||
- Start button active when name entered
|
||||
- Creates session with `metadata.project` = name
|
||||
|
||||
### Loading States
|
||||
|
||||
- Full-page overlay with spinner
|
||||
- Message: "Opening workspace..." or "Creating project..."
|
||||
- Duration: 300-800ms minimum for smooth UX
|
||||
|
||||
### Quick Actions (Card Menu)
|
||||
|
||||
- **Continue**: Navigate to IDE chat with this session
|
||||
- **Duplicate**: Create new session with same dir/metadata
|
||||
- **Delete**: Remove session with confirmation
|
||||
|
||||
## API Endpoints
|
||||
|
||||
### Existing (to be enhanced)
|
||||
|
||||
**GET `/claude/api/claude/sessions`**
|
||||
- Enhancement: Include `lastMessage` and `fileCount` in response
|
||||
- Calculate from `outputBuffer` server-side
|
||||
|
||||
**POST `/claude/api/claude/sessions`**
|
||||
- Enhancement: Accept `metadata.project` for custom naming
|
||||
|
||||
### New Endpoints
|
||||
|
||||
**PATCH `/claude/api/claude/sessions/:id`**
|
||||
- Update session metadata
|
||||
- Body: `{ metadata: { project: "New Name" } }`
|
||||
- Returns: Updated session object
|
||||
|
||||
**POST `/claude/api/claude/sessions/:id/duplicate`**
|
||||
- Create new session with same working directory and metadata
|
||||
- Returns: `{ success: true, session: { id, ... } }`
|
||||
|
||||
**DELETE `/claude/api/claude/sessions/:id`**
|
||||
- Delete session file from disk
|
||||
- Returns: `{ success: true }`
|
||||
|
||||
## Data Handling
|
||||
|
||||
### File Count Calculation
|
||||
|
||||
```javascript
|
||||
function getFileCount(session) {
|
||||
const writeTags = session.outputBuffer.filter(entry =>
|
||||
entry.content.includes('<dyad-write')
|
||||
);
|
||||
return writeTags.length;
|
||||
}
|
||||
```
|
||||
|
||||
### Relative Time Format
|
||||
|
||||
```javascript
|
||||
function getRelativeTime(date) {
|
||||
const seconds = Math.floor((new Date() - date) / 1000);
|
||||
if (seconds < 60) return 'just now';
|
||||
if (seconds < 3600) return `${Math.floor(seconds/60)} min ago`;
|
||||
if (seconds < 86400) return `${Math.floor(seconds/3600)} hours ago`;
|
||||
return `${Math.floor(seconds/86400)} days ago`;
|
||||
}
|
||||
```
|
||||
|
||||
### Session Cache
|
||||
|
||||
- Use Map for O(1) lookups
|
||||
- Invalidate on explicit refresh or error
|
||||
- Serve stale data with error banner on fetch failure
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Network Failures
|
||||
|
||||
1. **Session fetch fails**
|
||||
- Show: "Failed to load sessions. 🔄 Retry"
|
||||
- Keep stale data if cached
|
||||
- Auto-retry after 30s (exponential backoff)
|
||||
|
||||
2. **Session creation fails**
|
||||
- Hide loading overlay
|
||||
- Toast: "Failed to create project: [error]"
|
||||
- Stay on landing page
|
||||
|
||||
3. **Name update fails**
|
||||
- Revert to original name
|
||||
- Toast: "Failed to save name"
|
||||
|
||||
### Empty States
|
||||
|
||||
**No sessions:**
|
||||
```
|
||||
📁 No projects yet
|
||||
|
||||
Create your first project to start building with AI
|
||||
[Templates below...]
|
||||
```
|
||||
|
||||
**No active sessions:**
|
||||
- Show: "No active projects"
|
||||
- CTA: "Create New Project"
|
||||
- Still show historical sessions
|
||||
|
||||
### Validation Rules
|
||||
|
||||
- Project name: Max 50 chars, no `/ \ < > : " | ? *`
|
||||
- Trim whitespace
|
||||
- Show character count: "23/50"
|
||||
|
||||
### Race Conditions
|
||||
|
||||
- **Rapid clicking**: `isNavigating` flag, disable clicks during navigation
|
||||
- **Edit while deleted**: On save failure, refresh list, show "Session no longer exists"
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Backend Enhancements (1-2 hours)
|
||||
- [ ] Add `PATCH /sessions/:id` endpoint
|
||||
- [ ] Add `POST /sessions/:id/duplicate` endpoint
|
||||
- [ ] Add `DELETE /sessions/:id` endpoint
|
||||
- [ ] Enhance session list with `lastMessage` and `fileCount`
|
||||
|
||||
### Phase 2: Frontend Components (2-3 hours)
|
||||
- [ ] Refactor `sessions-landing.js` with state management
|
||||
- [ ] Create `SessionCard` class/component
|
||||
- [ ] Implement inline name editing
|
||||
- [ ] Add loading overlays
|
||||
|
||||
### Phase 3: Quick Start Enhancement (1 hour)
|
||||
- [ ] Add project name input to blank project card
|
||||
- [ ] Pass name to API on creation
|
||||
|
||||
### Phase 4: Polish (1 hour)
|
||||
- [ ] Relative time formatting
|
||||
- [ ] Card hover effects
|
||||
- [ ] Error toasts and retry
|
||||
- [ ] End-to-end testing
|
||||
|
||||
**Total Estimate:** 5-7 hours
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Create blank project with custom name
|
||||
- [ ] Create project from template
|
||||
- [ ] Edit project name inline
|
||||
- [ ] Continue to IDE chat
|
||||
- [ ] Duplicate session
|
||||
- [ ] Delete session
|
||||
- [ ] View empty session
|
||||
- [ ] View session with files
|
||||
- [ ] Network failure handling
|
||||
- [ ] Empty state display
|
||||
- [ ] Rapid clicking (race conditions)
|
||||
- [ ] Historical vs active display
|
||||
- [ ] Mobile responsive
|
||||
|
||||
**Browsers:** Chrome, Firefox, Safari
|
||||
**Screen Sizes:** Desktop (1920px), Tablet (768px), Mobile (375px)
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ Users can create/load projects in 1-2 clicks
|
||||
2. ✅ Navigation to IDE takes < 1 second perceived time
|
||||
3. ✅ All error states handled gracefully
|
||||
4. ✅ Project names are editable and persist
|
||||
5. ✅ Mobile-responsive design
|
||||
6. ✅ No data loss on network failures
|
||||
257
docs/plans/2025-01-19-project-session-organization-design.md
Normal file
257
docs/plans/2025-01-19-project-session-organization-design.md
Normal file
@@ -0,0 +1,257 @@
|
||||
# Project and Session Organization Design
|
||||
|
||||
**Date:** 2025-01-19
|
||||
**Status:** Approved
|
||||
**Author:** Claude (with user collaboration)
|
||||
|
||||
## Overview
|
||||
|
||||
Introduce persistent projects as first-class entities that contain multiple sessions, with intelligent assignment, reorganization, and soft-delete capabilities.
|
||||
|
||||
## Architecture
|
||||
|
||||
### Data Model
|
||||
|
||||
**Projects Collection**
|
||||
```javascript
|
||||
{
|
||||
_id: ObjectId,
|
||||
name: "My API Project",
|
||||
description: "REST API development",
|
||||
icon: "🚀",
|
||||
color: "#4a9eff",
|
||||
path: "/home/uroma/api",
|
||||
sessionIds: [ObjectId, ...],
|
||||
createdAt: Date,
|
||||
lastActivity: Date,
|
||||
deletedAt: Date | null // null = active, Date = in recycle bin
|
||||
}
|
||||
```
|
||||
|
||||
**Sessions Collection (Updated)**
|
||||
```javascript
|
||||
{
|
||||
...
|
||||
projectId: ObjectId | null, // null = unassigned
|
||||
deletedAt: Date | null
|
||||
}
|
||||
```
|
||||
|
||||
### API Endpoints
|
||||
|
||||
| Method | Endpoint | Description |
|
||||
|--------|----------|-------------|
|
||||
| GET | `/api/projects` | List active projects |
|
||||
| POST | `/api/projects` | Create project |
|
||||
| PUT | `/api/projects/:id` | Update project |
|
||||
| DELETE | `/api/projects/:id` | Soft delete (move to recycle bin) |
|
||||
| POST | `/api/projects/:id/restore` | Restore from bin |
|
||||
| DELETE | `/api/projects/:id/permanent` | Permanent delete |
|
||||
| GET | `/api/recycle-bin` | List deleted items |
|
||||
| POST | `/api/sessions/:id/move` | Move session to different project |
|
||||
|
||||
## UI Components
|
||||
|
||||
### Projects Page (`/projects`)
|
||||
|
||||
- **Header**: Title, "+ New Project" button, search bar
|
||||
- **Project Grid**: Cards with icon, name, description, path, session count, last activity
|
||||
- **Context Menu**: Edit, move to recycle bin
|
||||
- **Empty State**: "No projects yet" with CTA
|
||||
|
||||
### Enhanced Landing Page (`/claude/`)
|
||||
|
||||
Projects as top-level, sessions nested inside:
|
||||
- Collapsible project sections
|
||||
- "Unassigned Sessions" section at bottom
|
||||
- Session cards show project badge
|
||||
- Right-click for context menu
|
||||
|
||||
### Session Context Menu
|
||||
|
||||
```
|
||||
┌─────────────────────────┐
|
||||
│ Open in IDE │
|
||||
│ ─────────────────────── │
|
||||
│ Move to Project ▶ │
|
||||
│ ├── 🚀 My API (🎯 95%)│
|
||||
│ ├── 📱 Mobile App │
|
||||
│ ├── ──────────────────│
|
||||
│ └── Show All Projects │
|
||||
│ ─────────────────────── │
|
||||
│ Duplicate │
|
||||
│ Delete │
|
||||
└─────────────────────────┘
|
||||
```
|
||||
|
||||
### Recycle Bin
|
||||
|
||||
- Accessible from projects page header
|
||||
- Shows deleted projects with faded sessions
|
||||
- "Restore" and "Delete Permanently" buttons per item
|
||||
|
||||
## Smart Assignment Algorithm
|
||||
|
||||
### Auto-Assignment
|
||||
|
||||
When creating a session from the IDE project selector:
|
||||
1. User selects project → `projectId` stored
|
||||
2. Session added to project's `sessionIds` array
|
||||
3. Project `lastActivity` updated
|
||||
|
||||
### Smart Suggestions
|
||||
|
||||
Calculated when moving sessions:
|
||||
|
||||
```javascript
|
||||
function getSuggestions(session, allProjects) {
|
||||
const suggestions = [];
|
||||
|
||||
for (const project of allProjects) {
|
||||
let score = 0;
|
||||
let reasons = [];
|
||||
|
||||
// Directory matching (high weight)
|
||||
if (session.workingDir === project.path) {
|
||||
score += 90;
|
||||
reasons.push('Same directory');
|
||||
} else if (session.workingDir?.startsWith(project.path)) {
|
||||
score += 50;
|
||||
reasons.push('Subdirectory');
|
||||
}
|
||||
|
||||
// Recency (medium weight)
|
||||
const daysSinceActivity = (Date.now() - project.lastActivity) / (1000 * 60 * 60 * 24);
|
||||
if (daysSinceActivity < 1) {
|
||||
score += 20;
|
||||
reasons.push('Used today');
|
||||
} else if (daysSinceActivity < 7) {
|
||||
score += 10;
|
||||
reasons.push(`Used ${Math.floor(daysSinceActivity)} days ago`);
|
||||
}
|
||||
|
||||
// Name similarity (low weight)
|
||||
if (session.name?.includes(project.name) || project.name.includes(session.name)) {
|
||||
score += 15;
|
||||
reasons.push('Similar name');
|
||||
}
|
||||
|
||||
if (score > 0) {
|
||||
suggestions.push({ project, score, reasons });
|
||||
}
|
||||
}
|
||||
|
||||
return suggestions.sort((a, b) => b.score - a.score).slice(0, 3);
|
||||
}
|
||||
```
|
||||
|
||||
**Visual Indicators:**
|
||||
- 🎯 90%+ match → Same directory
|
||||
- 📂 50-89% match → Subdirectory or recent use
|
||||
- 💡 10-49% match → Similar name or recently used
|
||||
|
||||
## Recycle Bin System
|
||||
|
||||
### Soft Delete Flow
|
||||
|
||||
**Delete:**
|
||||
```javascript
|
||||
// Mark project as deleted
|
||||
await db.projects.updateOne(
|
||||
{ _id: projectId },
|
||||
{ $set: { deletedAt: new Date() } }
|
||||
);
|
||||
|
||||
// Soft delete all sessions in project
|
||||
await db.sessions.updateMany(
|
||||
{ projectId },
|
||||
{ $set: { deletedAt: new Date() } }
|
||||
);
|
||||
```
|
||||
|
||||
**Restore:**
|
||||
```javascript
|
||||
await db.projects.updateOne({ _id }, { $set: { deletedAt: null } });
|
||||
await db.sessions.updateMany({ projectId }, { $set: { deletedAt: null } });
|
||||
```
|
||||
|
||||
**Permanent Delete:**
|
||||
```javascript
|
||||
await db.projects.deleteOne({ _id });
|
||||
await db.sessions.deleteMany({ projectId });
|
||||
```
|
||||
|
||||
## Edge Cases
|
||||
|
||||
| Scenario | Behavior |
|
||||
|----------|----------|
|
||||
| Reassigning from deleted project | Session becomes unassigned (projectId = null) |
|
||||
| Deleting last session in project | Project persists with 0 sessions |
|
||||
| Two projects with same path | Both shown as suggestions with equal score |
|
||||
| Local CLI sessions | Can be assigned, path read from info.json |
|
||||
| Moving to deleted project | Blocked - deleted projects excluded from menu |
|
||||
| Concurrent edits | Last write wins (MongoDB atomic updates) |
|
||||
|
||||
## Implementation
|
||||
|
||||
### File Structure
|
||||
|
||||
```
|
||||
server.js (add endpoints)
|
||||
public/claude-ide/
|
||||
├── projects.html (new)
|
||||
├── projects.js (new)
|
||||
├── projects.css (new)
|
||||
└── sessions-landing.js (modify)
|
||||
context-menu.js (new shared component)
|
||||
```
|
||||
|
||||
### Migration Script
|
||||
|
||||
```javascript
|
||||
// 1. Create projects collection
|
||||
db.createCollection('projects');
|
||||
|
||||
// 2. Find unique project names from existing sessions
|
||||
const uniqueProjects = await db.sessions.distinct('metadata.project');
|
||||
|
||||
// 3. Create project for each unique name
|
||||
for (const name of uniqueProjects) {
|
||||
const projectSessions = await db.sessions.find({ 'metadata.project': name }).toArray();
|
||||
const paths = [...new Set(projectSessions.map(s => s.workingDir).filter(Boolean))];
|
||||
|
||||
await db.projects.insertOne({
|
||||
name,
|
||||
description: '',
|
||||
icon: getRandomIcon(),
|
||||
color: getRandomColor(),
|
||||
path: paths[0] || '',
|
||||
sessionIds: projectSessions.map(s => s._id),
|
||||
createdAt: projectSessions[0].createdAt,
|
||||
lastActivity: Math.max(...projectSessions.map(s => s.lastActivity)),
|
||||
deletedAt: null
|
||||
});
|
||||
}
|
||||
|
||||
// 4. Update sessions with projectId
|
||||
for (const project of await db.projects.find().toArray()) {
|
||||
await db.sessions.updateMany(
|
||||
{ 'metadata.project': project.name },
|
||||
{ $set: { projectId: project._id } }
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
- [ ] Create project via inline form
|
||||
- [ ] Auto-assign session on creation
|
||||
- [ ] Smart suggestions calculate correctly
|
||||
- [ ] Move session via context menu
|
||||
- [ ] Delete project → goes to recycle bin
|
||||
- [ ] Restore project from bin
|
||||
- [ ] Permanent delete works
|
||||
- [ ] Unassigned sessions display correctly
|
||||
- [ ] Migration backfills existing data
|
||||
- [ ] Icon/color randomization works
|
||||
- [ ] Path validation on project creation
|
||||
253
docs/plans/2025-01-19-simplified-landing-design.md
Normal file
253
docs/plans/2025-01-19-simplified-landing-design.md
Normal file
@@ -0,0 +1,253 @@
|
||||
# Simplified Landing Page Design
|
||||
|
||||
**Date:** 2025-01-19
|
||||
**Status:** Approved
|
||||
**Author:** Claude (with user collaboration)
|
||||
|
||||
## Overview
|
||||
|
||||
Create a Google-simple landing page that provides immediate access to create new projects or continue existing ones. The design philosophy is **speed and simplicity** - minimize friction for developers who want to get from idea → code as quickly as possible.
|
||||
|
||||
## Visual Layout
|
||||
|
||||
### Hero Section (Top Half)
|
||||
|
||||
**Centered Input Field**
|
||||
- Large, prominent input (600px wide)
|
||||
- Font size: 18-20px
|
||||
- Placeholder: "What project are you working on?"
|
||||
- Subtle border with blue glow on focus
|
||||
- Auto-complete suggests existing project names
|
||||
- Press Enter → creates session immediately
|
||||
|
||||
**Page Header**
|
||||
- Title: "Claude Code"
|
||||
- Subtitle: "Start coding"
|
||||
- Minimal, clean branding
|
||||
|
||||
**No Templates**
|
||||
- Templates removed from this view
|
||||
- Users can type "React todo app" or similar in the input
|
||||
- Keeps the page ultra-clean and focused
|
||||
|
||||
### Projects List (Bottom Half)
|
||||
|
||||
**Table Layout:**
|
||||
```
|
||||
| Project Name | Last Activity | Status | Actions |
|
||||
|--------------------|---------------|----------|----------------|
|
||||
| My React App | 2 hours ago | Active | [Continue] [⋮]|
|
||||
| API Server | 3 days ago | Done | [Continue] [⋮]|
|
||||
| Portfolio Website | 1 week ago | Archived | [Continue] [⋮]|
|
||||
```
|
||||
|
||||
**Columns:**
|
||||
1. **Project Name**: Session's `metadata.project` or fallback
|
||||
2. **Last Activity**: Relative time (2 hours ago, 3 days ago)
|
||||
3. **Status**: Badge showing "Active", "Done", or "Archived"
|
||||
4. **Actions**: "Continue" button + ⋮ menu
|
||||
|
||||
**Interactions:**
|
||||
- Click "Continue" button → go to IDE
|
||||
- Click ⋮ menu → Rename, Duplicate, Delete options
|
||||
- Click anywhere on row → also continues to IDE
|
||||
- Full row is clickable for speed
|
||||
|
||||
**Empty State:**
|
||||
- Text: "No projects yet. Type above to create your first one."
|
||||
|
||||
## Visual Design
|
||||
|
||||
**Color Scheme:**
|
||||
- Dark theme matching existing app
|
||||
- Background: #0d0d0d or similar
|
||||
- Table borders: subtle (#333)
|
||||
- Row hover: slightly lighter (#1a1a1a)
|
||||
|
||||
**Typography:**
|
||||
- Project names: Bold, 16px
|
||||
- Last activity: 14px, gray
|
||||
- Status badges: 12px, rounded
|
||||
|
||||
**Status Badge Colors:**
|
||||
- Active: Green (#51cf66)
|
||||
- Done: Gray (#888)
|
||||
- Archived: Orange (#ffa94d)
|
||||
|
||||
**Buttons:**
|
||||
- Continue: Gradient (blue #4a9eff → purple #a78bfa)
|
||||
- Menu (⋮): Gray, circular, minimal
|
||||
|
||||
**Responsive Design:**
|
||||
- Desktop (>768px): Full table
|
||||
- Tablet (≤768px): Columns collapse, simpler layout
|
||||
- Mobile (≤480px): Cards instead of table
|
||||
|
||||
## User Flow
|
||||
|
||||
### Create New Project
|
||||
1. User types project name in input field
|
||||
2. User presses Enter (or clicks Start button)
|
||||
3. Show "Creating..." overlay (300-800ms minimum)
|
||||
4. Redirect to `/claude/ide?session={new-id}`
|
||||
5. User can immediately chat with Claude
|
||||
|
||||
### Continue Existing Project
|
||||
1. User clicks Continue button or project row
|
||||
2. Show "Opening..." overlay (300-800ms minimum)
|
||||
3. Redirect to `/claude/ide?session={existing-id}`
|
||||
4. User resumes where they left off
|
||||
|
||||
### Quick Actions (⋮ Menu)
|
||||
- **Rename**: Inline edit the project name
|
||||
- **Duplicate**: Create copy of session
|
||||
- **Delete**: Remove session with confirmation
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
### Page Structure
|
||||
|
||||
```
|
||||
<body>
|
||||
<div class="landing-container">
|
||||
<!-- Hero Section -->
|
||||
<section class="hero">
|
||||
<h1>Claude Code</h1>
|
||||
<p class="subtitle">Start coding</p>
|
||||
<input type="text" class="project-input" placeholder="What project are you working on?" />
|
||||
</section>
|
||||
|
||||
<!-- Projects Section -->
|
||||
<section class="projects">
|
||||
<table class="projects-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Project Name</th>
|
||||
<th>Last Activity</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<!-- Project rows -->
|
||||
</tbody>
|
||||
</table>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
```
|
||||
|
||||
### Data Requirements
|
||||
|
||||
**API Endpoints:**
|
||||
- `GET /claude/api/claude/sessions` - List all sessions (already exists)
|
||||
- `POST /claude/api/claude/sessions` - Create new session (already exists)
|
||||
- `PATCH /claude/api/claude/sessions/:id` - Update metadata (already exists)
|
||||
- `POST /claude/api/claude/sessions/:id/duplicate` - Duplicate (already exists)
|
||||
- `DELETE /claude/api/claude/sessions/:id` - Delete (already exists)
|
||||
|
||||
**Session Object Structure:**
|
||||
```javascript
|
||||
{
|
||||
id: "session-xxx",
|
||||
metadata: {
|
||||
project: "My Project Name",
|
||||
type: "chat",
|
||||
source: "web-ide"
|
||||
},
|
||||
lastActivity: "2025-01-19T10:30:00Z",
|
||||
status: "running" | "terminated",
|
||||
createdAt: "2025-01-19T09:00:00Z"
|
||||
}
|
||||
```
|
||||
|
||||
### Behavior Requirements
|
||||
|
||||
**Input Field:**
|
||||
- Focus on page load
|
||||
- Create on Enter key
|
||||
- Auto-complete with existing project names
|
||||
- Validation: No invalid characters (`/ \ < > : " | ? *`)
|
||||
- Max length: 50 characters
|
||||
|
||||
**Projects Table:**
|
||||
- Sort by last activity (newest first)
|
||||
- Show all sessions (active and historical)
|
||||
- Status derived from `session.status`
|
||||
- Relative time calculated from `lastActivity` or `createdAt`
|
||||
|
||||
**Loading States:**
|
||||
- Overlay with spinner
|
||||
- Messages: "Creating project..." or "Opening workspace..."
|
||||
- Minimum 300ms display for perceived smoothness
|
||||
|
||||
**Error Handling:**
|
||||
- Network failures: Show toast error
|
||||
- Validation errors: Show inline message
|
||||
- Empty project name: Disable Start button
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ Users can create a project in one action (type name + Enter)
|
||||
2. ✅ Users can continue a project in one click
|
||||
3. ✅ Navigation to IDE takes < 1 second perceived time
|
||||
4. ✅ Page is visually clean and minimal (Google-like)
|
||||
5. ✅ All existing functionality preserved (rename, duplicate, delete)
|
||||
6. ✅ Mobile-responsive design
|
||||
|
||||
## Files to Modify
|
||||
|
||||
1. **HTML**: `/public/claude-landing.html`
|
||||
- Replace current layout with new hero + table structure
|
||||
|
||||
2. **CSS**: `/public/claude-ide/sessions-landing.css`
|
||||
- Add hero section styles
|
||||
- Add table/grid layout styles
|
||||
- Remove old card-based styles
|
||||
|
||||
3. **JavaScript**: `/public/claude-ide/sessions-landing.js`
|
||||
- Simplify to render table instead of cards
|
||||
- Add input focus handler
|
||||
- Keep existing API calls and logic
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
**YAGNI Principle:**
|
||||
- Remove template cards (React, Node.js, etc.) - not needed
|
||||
- Remove "Create New Project" vs "Load Existing Project" separation
|
||||
- Single input field = simpler UX
|
||||
- Table view = cleaner than cards
|
||||
|
||||
**Performance:**
|
||||
- Minimal DOM manipulation
|
||||
- Table rows render quickly
|
||||
- No complex animations
|
||||
- Fast page load
|
||||
|
||||
**Accessibility:**
|
||||
- Keyboard navigation (Tab through table, Enter to continue)
|
||||
- ARIA labels on buttons
|
||||
- Focus indicators visible
|
||||
- Screen reader friendly
|
||||
|
||||
## Migration from Current Design
|
||||
|
||||
**What's Removed:**
|
||||
- Template quick-start cards
|
||||
- "Create New Project" section with cards
|
||||
- "Load Existing Project" section header
|
||||
- Divider ("or")
|
||||
- SessionCard component (replaced by table rows)
|
||||
|
||||
**What's Kept:**
|
||||
- All API endpoints
|
||||
- All session data
|
||||
- Rename, duplicate, delete functionality
|
||||
- Loading overlays
|
||||
- Toast notifications
|
||||
- Input validation logic
|
||||
|
||||
**What's Simplified:**
|
||||
- Single create flow (just type and go)
|
||||
- Unified projects list (no active/historical separation needed visually)
|
||||
- Cleaner visual hierarchy
|
||||
394
docs/plans/2025-01-19-web-terminal-integration.md
Normal file
394
docs/plans/2025-01-19-web-terminal-integration.md
Normal file
@@ -0,0 +1,394 @@
|
||||
# Web-Based Terminal Integration for Claude Code CLI
|
||||
|
||||
**Date:** 2025-01-19
|
||||
**Status:** Designed
|
||||
**Author:** Claude (with user collaboration)
|
||||
|
||||
## Overview
|
||||
|
||||
Add full-featured web-based terminals to the Claude Code web interface, allowing users to open multiple terminal tabs that can run independently or attach to Claude Code sessions. Users get full interactive shell access with xterm.js, powered by node-pty and WebSocket communication.
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
**Power + Simplicity:** Provide full shell capabilities (as user already has SSH access) with intelligent defaults and easy restoration. No artificial restrictions - the user is the developer and knows what they're doing.
|
||||
|
||||
## Core Features
|
||||
|
||||
### 1. Terminal Panel
|
||||
- **Location:** Dedicated "🖥️ Terminal" tab in IDE navbar (next to Chat, Files, etc.)
|
||||
- **Multi-tab:** Open unlimited terminals, switch between them
|
||||
- **Tab information:** Shows session ID, mode, current directory
|
||||
- **Controls:** New terminal, close, mode toggle, clear screen
|
||||
|
||||
### 2. Terminal Modes
|
||||
- **Session Mode (🔵):** View Claude Code CLI output (stdio/stderr from session process)
|
||||
- **Shell Mode (🟢):** Independent shell for running commands
|
||||
- **Mixed Mode (🟡):** Both session output AND shell access (default)
|
||||
|
||||
### 3. Directory Selection
|
||||
- Modal picker when creating new terminal
|
||||
- Shows recent directories (last 5 used)
|
||||
- Custom path input
|
||||
- All terminals inherit chosen working directory
|
||||
|
||||
### 4. Session Attachment
|
||||
- Attach any terminal to a Claude Code session
|
||||
- See what Claude "sees" - file operations, tool output
|
||||
- Switch between viewing session and running commands
|
||||
- Perfect for debugging and understanding Claude's actions
|
||||
|
||||
### 5. Auto-Restore
|
||||
- **Always saves** terminal setup automatically
|
||||
- On page reload/refresh: all terminals close
|
||||
- Shows: "📝 Previous session: 3 terminal(s) [Restore All] [Dismiss]"
|
||||
- One-click restore recreates terminals with same configuration
|
||||
- Fresh PTY processes, but same directories/sessions/modes
|
||||
|
||||
### 6. Security & Safety
|
||||
- **Warning modal** on first terminal use (can be dismissed)
|
||||
- **Full shell access** - no artificial restrictions (user has SSH anyway)
|
||||
- **All commands logged** to `.claude-ide/terminal-logs.jsonl`
|
||||
- **No sudo blocking** - user controls their own system
|
||||
- **Processes killed** on page close (no orphans)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Frontend Components
|
||||
|
||||
```
|
||||
Terminal View (new tab)
|
||||
├── Terminal Header
|
||||
│ ├── Terminal Tabs (list of open terminals)
|
||||
│ └── [+ New Terminal] button
|
||||
└── Active Terminal Container
|
||||
├── Terminal Toolbar
|
||||
│ ├── Terminal info (ID, path, mode)
|
||||
│ └── Controls (mode toggle, clear, close)
|
||||
└── xterm.js Container
|
||||
```
|
||||
|
||||
### Backend Architecture
|
||||
|
||||
```
|
||||
Frontend (xterm.js)
|
||||
↓ WebSocket
|
||||
TerminalController (Node.js)
|
||||
↓ spawn + pty
|
||||
Pseudo-terminal (node-pty)
|
||||
↓ bidirectional I/O
|
||||
├── Shell Process (bash/zsh)
|
||||
└── OR Claude Session Process
|
||||
```
|
||||
|
||||
### Data Flow
|
||||
|
||||
**Creating a Terminal:**
|
||||
1. User clicks "+ New Terminal"
|
||||
2. Directory picker modal shows
|
||||
3. User selects directory
|
||||
4. POST `/claude/api/terminals` with `{ workingDir, sessionId?, mode }`
|
||||
5. Backend creates PTY with shell in working directory
|
||||
6. Returns terminal ID
|
||||
7. Frontend opens WebSocket: `ws://host/claude/api/terminals/{id}/ws`
|
||||
8. xterm.js initializes, begins receiving/sending I/O
|
||||
9. If attached to session, pipe session output to PTY
|
||||
|
||||
**Terminal I/O Loop:**
|
||||
```
|
||||
User types in xterm.js
|
||||
↓ WebSocket send
|
||||
Backend receives message
|
||||
↓ Write to PTY
|
||||
Shell/Claude process executes
|
||||
↓ PTY emits data
|
||||
Backend reads PTY output
|
||||
↓ WebSocket send
|
||||
xterm.js displays data
|
||||
```
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
### New Dependencies
|
||||
|
||||
```bash
|
||||
npm install xterm xterm-addon-fit xterm-addon-web-links node-pty @ws/websocket
|
||||
```
|
||||
|
||||
### Backend Files
|
||||
|
||||
**`/services/terminal-service.js`** - Core terminal management
|
||||
```javascript
|
||||
class TerminalService {
|
||||
constructor()
|
||||
createServer(server) // Setup WebSocket server
|
||||
createTerminal(options) // Create PTY with shell
|
||||
handleConnection(terminalId, ws) // WebSocket I/O handling
|
||||
attachToSession(terminal, session) // Pipe session output to PTY
|
||||
closeTerminal(terminalId) // Kill PTY process
|
||||
}
|
||||
```
|
||||
|
||||
**New API Endpoints:**
|
||||
- `POST /claude/api/terminals` - Create terminal
|
||||
- `DELETE /claude/api/terminals/:id` - Close terminal
|
||||
- `GET /claude/api/terminals` - List active terminals
|
||||
- `POST /claude/api/terminals/:id/attach` - Attach to session
|
||||
- `GET /claude/api/claude/terminal-restore` - Get saved state
|
||||
- `GET /claude/api/files/recent-dirs` - Get recent directories
|
||||
|
||||
### Frontend Files
|
||||
|
||||
**`/public/claude-ide/terminal.js`** - Terminal manager
|
||||
```javascript
|
||||
class TerminalManager {
|
||||
loadXTerm() // Load xterm CSS dynamically
|
||||
createTerminal(options) // Create terminal UI + WebSocket
|
||||
switchToTerminal(id) // Switch active terminal
|
||||
closeTerminal(id) // Close terminal
|
||||
setMode(id, mode) // Change session/shell/mixed mode
|
||||
restoreTerminals(savedState) // Restore previous session
|
||||
}
|
||||
```
|
||||
|
||||
**Modifications to `/public/claude-ide/ide.js`:**
|
||||
- Add terminal view handling
|
||||
- Add restore prompt logic
|
||||
- Integrate with existing navigation
|
||||
|
||||
**Modifications to `/public/claude-ide/index.html`:**
|
||||
- Add Terminal tab to navbar
|
||||
- Add terminal-view div to main content
|
||||
|
||||
## Key Features Detail
|
||||
|
||||
### Mode Switching
|
||||
|
||||
**Session Mode:**
|
||||
- Shows only Claude Code CLI output
|
||||
- Read-only view into what Claude does
|
||||
- Useful for debugging tool usage, file operations
|
||||
|
||||
**Shell Mode:**
|
||||
- Independent bash/zsh shell
|
||||
- User types commands directly
|
||||
- No session output shown
|
||||
- Like opening a terminal anywhere else
|
||||
|
||||
**Mixed Mode (Default):**
|
||||
- Shows both session output AND shell commands
|
||||
- User can type commands while seeing Claude's work
|
||||
- Perfect for development workflow
|
||||
- Toggle which output is visible
|
||||
|
||||
### Session Attachment
|
||||
|
||||
When attaching terminal to a session:
|
||||
- Backend creates pipe from session.stdout to PTY input
|
||||
- Session.stderr shown in red color
|
||||
- Session messages appear interleaved with shell output
|
||||
- User can interact with both simultaneously
|
||||
|
||||
### Directory Picker
|
||||
|
||||
Shows recently used directories:
|
||||
- Current session's working directory
|
||||
- Last 5 directories used in any terminal
|
||||
- Obsidian vault paths
|
||||
- User can type custom path
|
||||
|
||||
### State Persistence
|
||||
|
||||
**Saved in `.claude-ide/terminal-state.json`:**
|
||||
```json
|
||||
{
|
||||
"timestamp": "2025-01-19T14:30:00Z",
|
||||
"terminals": [
|
||||
{
|
||||
"id": "term-1",
|
||||
"workingDir": "/home/uroma/obsidian-vault",
|
||||
"sessionId": "session-abc",
|
||||
"mode": "mixed",
|
||||
"order": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
**Restoration:**
|
||||
- On page load, check for saved state
|
||||
- If found, show non-intrusive toast
|
||||
- "Restore All" recreates terminals (new PTY processes)
|
||||
- "Dismiss" ignores saved state
|
||||
- Toast auto-dismisses after 30 seconds
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Logging
|
||||
|
||||
All commands logged to `/home/uroma/obsidian-vault/.claude-ide/terminal-logs.jsonl`:
|
||||
```json
|
||||
{"timestamp":"2025-01-19T14:30:00Z","terminalId":"term-1","command":"ls -la","user":"uroma"}
|
||||
```
|
||||
|
||||
### First-Time Warning
|
||||
|
||||
Modal shown on first terminal use:
|
||||
```
|
||||
⚠️ Terminal Access
|
||||
You're about to open a full shell terminal.
|
||||
This gives you complete command-line access.
|
||||
|
||||
[ ] Don't show this warning again
|
||||
|
||||
[Cancel] [Open Terminal]
|
||||
```
|
||||
|
||||
### Resource Limits
|
||||
|
||||
- No hard limit on terminal count (practical limit ~10)
|
||||
- Each terminal ~10-20MB memory
|
||||
- All terminals killed on:
|
||||
- Page close
|
||||
- Browser crash
|
||||
- Server restart
|
||||
- User logout
|
||||
|
||||
## UI Design
|
||||
|
||||
### Terminal Panel Layout
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ 🖥️ Terminals [+ New] │
|
||||
├─────────────────────────────────────────────────────────┤
|
||||
│ ┌───────────────────────────────────────────────────┐ │
|
||||
│ │ session-abc123 │ ▀ │ 🟡 Mixed │ ~/obsidian-vault │ │
|
||||
│ │ [Close] [Split] │ │
|
||||
│ ├───────────────────────────────────────────────────┤ │
|
||||
│ │ $ npm test │ │
|
||||
│ │ Tests running... │ │
|
||||
│ │ ✓ Test 1 passed │ │
|
||||
│ │ ✓ Test 2 passed │ │
|
||||
│ │ │ │
|
||||
│ └───────────────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ [Terminal Tab 2] [Terminal Tab 3] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Keyboard Shortcuts
|
||||
|
||||
- `Ctrl+Shift+T` - Open new terminal
|
||||
- `Ctrl+Shift+W` - Close current terminal
|
||||
- `Ctrl+Tab` - Next terminal tab
|
||||
- `Ctrl+Shift+Tab` - Previous terminal tab
|
||||
- `Ctrl+Shift+M` - Toggle terminal mode
|
||||
|
||||
### Color Scheme
|
||||
|
||||
xterm.js theme matching existing IDE:
|
||||
- Background: `#1a1a1a`
|
||||
- Foreground: `#e0e0e0`
|
||||
- Cursor: `#4a9eff`
|
||||
- Selection: `rgba(74, 158, 255, 0.3)`
|
||||
- Session stderr: Red `\x1b[31m`
|
||||
|
||||
## Implementation Plan
|
||||
|
||||
### Phase 1: Backend Setup
|
||||
1. Install dependencies: `xterm`, `node-pty`, `ws`
|
||||
2. Create `terminal-service.js`
|
||||
3. Add API endpoints to `server.js`
|
||||
4. Implement WebSocket server
|
||||
5. Test terminal creation and I/O
|
||||
|
||||
### Phase 2: Frontend Terminal Panel
|
||||
1. Add terminal view to `index.html`
|
||||
2. Implement `terminal.js` with xterm.js integration
|
||||
3. Create directory picker modal
|
||||
4. Implement tab management
|
||||
5. Add keyboard shortcuts
|
||||
|
||||
### Phase 3: Session Integration
|
||||
1. Implement session attachment logic
|
||||
2. Add mode switching (session/shell/mixed)
|
||||
3. Pipe session output to PTY
|
||||
4. Test with active Claude Code sessions
|
||||
|
||||
### Phase 4: Polish & Features
|
||||
1. Implement auto-save/restore
|
||||
2. Add first-time warning modal
|
||||
3. Implement command logging
|
||||
4. Add recent directories tracking
|
||||
5. Test edge cases
|
||||
|
||||
### Phase 5: Testing
|
||||
1. Test multiple terminals simultaneously
|
||||
2. Test session attachment
|
||||
3. Test mode switching
|
||||
4. Test restore after page refresh
|
||||
5. Test with various commands (vim, htop, etc.)
|
||||
|
||||
## Success Criteria
|
||||
|
||||
1. ✅ Can open unlimited terminal tabs
|
||||
2. ✅ Each terminal runs in chosen directory
|
||||
3. ✅ Can attach terminal to Claude Code session
|
||||
4. ✅ Can switch between session/shell/mixed modes
|
||||
5. ✅ Terminal I/O is responsive (no lag)
|
||||
6. ✅ Terminals restore after page refresh
|
||||
7. ✅ All commands are logged
|
||||
8. ✅ Works with existing sessions
|
||||
9. ✅ Keyboard shortcuts work correctly
|
||||
10. ✅ No memory leaks or orphaned processes
|
||||
|
||||
## Future Enhancements (Not in MVP)
|
||||
|
||||
- **Split terminals** - Divide terminal panes vertically/horizontally
|
||||
- **Terminal sharing** - Share terminal URL with collaborators
|
||||
- **Command history** - Show history of commands across terminals
|
||||
- **Search in terminal** - Search/backscroll through output
|
||||
- **Download output** - Save terminal output to file
|
||||
- **Multiple shells** - Support zsh, fish, pwsh, etc.
|
||||
- **Custom fonts/themes** - User-configurable appearance
|
||||
|
||||
## Files to Create
|
||||
|
||||
1. `/services/terminal-service.js` - Terminal backend logic
|
||||
2. `/public/claude-ide/terminal.js` - Terminal frontend manager
|
||||
3. `/public/claude-ide/terminal.css` - Terminal panel styles
|
||||
4. `/home/uroma/obsidian-vault/.claude-ide/terminal-logs.jsonl` - Command log
|
||||
|
||||
## Files to Modify
|
||||
|
||||
1. `/server.js` - Add terminal API endpoints
|
||||
2. `/public/claude-ide/index.html` - Add Terminal tab and view
|
||||
3. `/public/claude-ide/ide.js` - Add terminal view navigation
|
||||
|
||||
## Dependencies
|
||||
|
||||
```json
|
||||
{
|
||||
"xterm": "^5.3.0",
|
||||
"xterm-addon-fit": "^0.8.0",
|
||||
"xterm-addon-web-links": "^0.9.0",
|
||||
"node-pty": "^1.0.0",
|
||||
"ws": "^8.16.0"
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- **PTY processes** cannot be serialized, so restoration creates new processes
|
||||
- **WebSocket reconnection** is supported if network drops temporarily
|
||||
- **Process cleanup** happens automatically on page close/navigation
|
||||
- **Memory usage** per terminal is ~10-20MB (practical limit ~50 terminals)
|
||||
- **CPU usage** is minimal when terminals are idle
|
||||
- **Security** is maintained by session-based authentication
|
||||
- **Logging** is for debugging and auditing, not surveillance
|
||||
|
||||
---
|
||||
|
||||
**Design Status:** ✅ Complete and approved
|
||||
**Ready for Implementation:** Yes
|
||||
**Estimated Implementation Time:** 3-4 days (5 phases)
|
||||
Reference in New Issue
Block a user