Files
opencode-krates-connector/design/shell-logs.md
Hermes Agent 78f19cde7d feat: Add comprehensive design documentation for Krates
- Canvas: Infinite zoomable workspace with LOD and navigation
- Spotlight: Fuzzy search with type filters and view shortcuts
- Krate: Window group container with non-overlapping placement
- Detail Window: YAML/Describe/Logs/Shell views with maximize
- Top Bar: Cluster info, user presence, admin toggle
- Admin Drawer: Multi-user presence and spectate functionality
- Minimap: Browse and navigate canvas overview
- Collection Window: List/tree views with filtering and sorting
- Shell/Logs: Real-time terminal and log streaming
- Backend: Go service with K8s API, WebSocket handlers, CRDT sync
- Architecture: Full project structure and tech stack
2026-06-16 08:32:47 -04:00

338 lines
8.5 KiB
Markdown

# Shell & Logs Windows Feature Specification
## Overview
Shell and Logs windows are specialized detail windows that stream real-time data from Kubernetes pods. Shell provides an interactive terminal, while Logs shows container output.
---
## Shell Window
### Purpose
Interactive pseudo-terminal session inside the application, connected to a pod via `kubectl exec`.
### Implementation
#### Client-Side (xterm.js)
```typescript
// Initialization
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
const term = new Terminal({
rows: 24,
cols: 80,
fontFamily: 'IBM Plex Mono',
fontSize: 12,
theme: {
background: '#0b0e13',
foreground: '#e6edf6',
cursor: '#4dd6e8'
}
});
const fitAddon = new FitAddon();
term.loadAddon(fitAddon);
fitAddon.fit();
```
#### WebSocket Connection
```typescript
// On krate creation with shell tab
const ws = new WebSocket(`/ws/shell?pod=${pod}&ns=${ns}`);
ws.onopen = () => {
term.focus();
};
ws.onmessage = (event) => {
term.write(event.data); // stdout/stderr
};
term.onData((data) => {
ws.send(data); // stdin (keypresses, etc.)
});
term.onResize(({ rows, cols }) => {
ws.send(JSON.stringify({ type: 'resize', rows, cols }));
});
```
### Keyboard Routing
#### Mouse-Over Focus
- Track `mouseenter`/`mouseleave` per window
- Set `hoveredWindowId` on shell windows
- Global `keydown` handler routes to shell when focused
#### Critical: Space Key
- **Must NOT open spotlight** when shell has focus
- Shell window handles space as input (terminal command)
- Implementation:
```typescript
document.addEventListener('keydown', (e) => {
if (hoveredWindowId && isShellWindow(hoveredWindowId)) {
// Space goes to shell
return; // Don't trigger canvas shortcuts
}
// Handle canvas shortcuts (space pan, etc.)
}, { capture: true });
```
### Scroll Wheel Behavior
- On shell content: Scroll terminal (not canvas)
- On empty canvas: Pan
- **Ctrl/⌘ + scroll over shell**: Zoom canvas (not terminal zoom)
- Use `{ capture: true }` on canvas wheel handler
- Check `e.ctrlKey || e.metaKey`
- If true: zoom canvas; otherwise: let event propagate
### Terminal Features
- **Full PTY emulation**: Support all ANSI escapes
- **Resize**: Send new size on container resize
- **Paste**: Support Ctrl+Shift+V or Shift+Insert
- **Copy**: Ctrl+Shift+C or Ctrl+C (custom)
- **Search**: `/` to search, `n`/`N` for next/prev
- **Font scaling**: Ctrl+wheel for terminal font size
### Backend Implementation (Go)
```go
// WebSocket connection
func (s *Server) handleShell(w http.ResponseWriter, r *http.Request) {
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
defer ws.Close()
pod := r.URL.Query().Get("pod")
ns := r.URL.Query().Get("ns")
// Create k8s exec request
req := s.client.CoreV1().RESTClient().Post().
Name(pod).Namespace(ns).Resource("pods").SubResource("exec")
req.VersionedParams(&v1.ExecOptions{
Stdin: true,
Stdout: true,
Stderr: true,
Terminal: true,
}, scheme.ParameterCodec)
exec, err := remotecommand.NewSPDYExecutor(s.config, "POST", req.URL())
if err != nil {
ws.Close()
return
}
// Stream between WebSocket and exec
exec.Stream(remotecommand.StreamOptions{
Stdin: ws,
Stdout: ws,
Stderr: ws,
TermSize: &remotecommand.TerminalSize{
Width: 80,
Height: 24,
},
})
}
```
---
## Logs Window
### Purpose
Live-tailing container logs, showing output in real-time.
### Streaming
```typescript
// WebSocket connection
const ws = new WebSocket(`/ws/logs?pod=${pod}&ns=${ns}`);
ws.onmessage = (event) => {
const line = event.data;
addLogLine(line);
};
function addLogLine(line: string) {
const entry = parseLogLine(line);
logBuffer.push(entry);
// Auto-scroll if at bottom
if (atBottom()) {
scrollToBottom();
}
}
```
### Auto-Scroll Behavior
- **New lines arrive**: Auto-scroll to bottom
- **User scrolls up**: Stop auto-scrolling
- **Detection**:
```typescript
function atBottom(): boolean {
const threshold = 50; // px
return (scrollTop + clientHeight + threshold) >= scrollHeight;
}
```
### Color Coding
Parse lines and color-code by pattern:
```typescript
function parseLogLine(line: string): LogEntry {
// Pattern matching for common log levels
if (line.includes('ERROR') || line.includes('ERR')) {
return { text: line, color: '#ef6f6f' }; // Red
} else if (line.includes('WARN') || line.includes('WARNING')) {
return { text: line, color: '#e8b54a' }; // Amber
} else if (line.includes('INFO') || line.includes('I ')) {
return { text: line, color: '#b9c6d8' }; // Default
} else {
return { text: line, color: '#c7d2e0' }; // Muted
}
}
```
### Timestamps
- **Default**: Include timestamps
- **Toggle**: Optional button to show/hide
- **Format**: ISO 8601 or container format
### Scrollbar Styling
- Width: 8px
- Track: rgba(140,165,200,.1)
- Thumb: rgba(140,165,200,.3)
- Hover thumb: rgba(140,165,200,.5)
### Content Rendering
```typescript
<pre style={{ overflow: 'auto', height: '286px', fontFamily: 'IBM Plex Mono' }}>
{logs.map((line, i) => (
<div key={i} style={{ color: line.color }}>
{line.timestamp && <span className="timestamp">{line.timestamp} </span>}
{line.text}
</div>
))}
</pre>
```
---
## View-Specific Behavior
### Shell Tab
- **Enabled for**: Pods, and workloads that support exec
- **Disabled for**: Deployments, Services, etc.
- **Visual**: `opacity: 0.45` for disabled
- **Label**: Show "shell" but don't route to it
### Logs Tab
- **Enabled for**: Pods and workloads ( Deployments, StatefulSets, DaemonSets)
- **Disabled for**: ConfigMaps, Secrets, PVCs, etc.
- **Visual**: Same as shell
---
## Keyboard Shortcuts
| Key | Context | Action |
|---|---|---|
| ↑ / ↓ | Logs shell focused | Navigate scroll (logs) / line history (shell) |
| PageUp / PageDown | Logs focused | Page scroll |
| Ctrl+L | Logs focused | Clear screen (only in shell) |
| Ctrl+C | Shell focused | Send interrupt to shell |
| Ctrl+Z | Shell focused | Send suspend (if supported) |
| Ctrl+Shift+F | Logs focused | Find (future enhancement) |
| Ctrl+Shift+V | Shell focused | Paste |
| Ctrl+Shift+C | Shell focused | Copy selection |
---
## Error Handling
### Connection Errors
- **Shell**: Show error border, keep terminal buffer, retry button
- **Logs**: Show error indicator, auto-reconnect with backoff
### Reconnection Strategy
```typescript
let retries = 0;
const maxRetries = 5;
function reconnect() {
if (retries >= maxRetries) {
showFatalError('Connection failed');
return;
}
setTimeout(() => {
retries++;
connect();
}, Math.min(1000 * 2^retries, 10000)); // Exponential backoff
}
```
### Buffer Management
- **Shell**: Keep last 1000 lines in buffer
- **Logs**: Keep last 2000 lines
- **Overflow**: Drop oldest lines, mark with "..." indicator
---
## State Management
### Shell State
```typescript
interface ShellState {
connected: boolean;
rows: number;
cols: number;
buffer: string[]; // Last N lines
cursor: { x, y };
}
```
### Logs State
```typescript
interface LogsState {
lines: LogEntry[];
scrollTop: number;
autoScroll: boolean;
timestamp: boolean;
}
```
---
## Gotchas
### Critical Issues
1. **Space key routing**: Must distinguish between shell space (input) and canvas space-pan
2. **Ctrl/⌘+scroll**: Intercept on canvas with `{ capture: true }`, check modifier
3. **Terminal resize**: Send `TermSize` on window resize events
4. **Paste security**: Sanitize pasted content, avoid escape sequences
5. **Cursor position**: xterm.js tracks cursor; keep in sync with server
6. **Buffer overflow**: Implement LRU buffer, don't memory leak
### Performance
1. **Throttle updates**: Don't render on every log line, batch
2. **Virtual scrolling**: For logs with many lines
3. **Web Worker**: Offload parsing to worker for large logs
### Animation
1. **Blinking cursor**: CSS animation or xterm.js built-in
2. **Auto-scroll indicator**: Small arrow when scrolled up
3. **Connection status**: Green/amber/red dot
## Testing Checklist
- [ ] Shell key press → terminal receives it
- [ ] Terminal output → WebSocket → UI
- [ ] Resize → terminal resizes correctly
- [ ] Space in shell ≠ spotlight open
- [ ] Ctrl+scroll over shell ≠ zoom canvas
- [ ] Logs auto-scroll works (and stops on manual scroll)
- [ ] Connection loss → retry with backoff
- [ ] Buffer overflow → old lines dropped
- [ ] Shell interrupt (Ctrl+C) handled
- [ ] Paste works (Ctrl+Shift+V)