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
This commit is contained in:
176
design/detail-window.md
Normal file
176
design/detail-window.md
Normal file
@@ -0,0 +1,176 @@
|
||||
# Detail Window Feature Specification
|
||||
|
||||
## Overview
|
||||
A detail window shows one view (YAML / Describe / Logs / Shell) for one Kubernetes object. Windows are grouped into krates and can be resized/moved within their container.
|
||||
|
||||
## Core Responsibilities
|
||||
- Display object data in one of four view types
|
||||
- Allow window dragging within krate
|
||||
- Support window resizing with proportional sibling adjustment
|
||||
- Implement maximize/restore functionality
|
||||
- Route keyboard focus on mouse-over
|
||||
|
||||
## Window Types
|
||||
|
||||
### Detail View (default)
|
||||
- **Purpose**: Display static object information
|
||||
- **Content types**: YAML, Describe, Logs (for pods/workloads)
|
||||
|
||||
### Collection Window
|
||||
- **Purpose**: List/Tree view of multiple objects
|
||||
- **Special features**: Filter input, selection highlighting, view shortcuts
|
||||
|
||||
## Window anatomy (380×362px default)
|
||||
|
||||
### Header (36px height)
|
||||
- Left: Shape glyph (8px, color-coded by object type)
|
||||
- Middle: View label (uppercase, accent-colored) + object name + status badge
|
||||
- Right: × button (close window)
|
||||
- **Cursor**: `grab` — drag to reposition within canvas
|
||||
- **Double-click header**: Maximize / restore window
|
||||
- **Drag**: Move window within krate
|
||||
|
||||
### Tab Bar
|
||||
- Tabs: YAML · Describe · Logs · Shell
|
||||
- Shell tab: disabled (opacity 0.45) for non-exec object types
|
||||
- Active tab: accent underline/highlight
|
||||
- Click tab: Switch view content
|
||||
|
||||
### Content Area (286px height)
|
||||
- Element: `<pre>` (preformatted text)
|
||||
- Styling:
|
||||
- `overflow: auto`
|
||||
- `height: 286px`
|
||||
- `font-family: 'IBM Plex Mono'`
|
||||
- `font-size: 12px`
|
||||
- `line-height: 1.65`
|
||||
- Scrollbar:
|
||||
- Width: 8px
|
||||
- Color: rgba(140,165,200,.18)
|
||||
|
||||
## Resize Behavior
|
||||
|
||||
### Resize Handle
|
||||
- Positioned at bottom-right corner
|
||||
- Drag to resize
|
||||
- **Proportional adjustment**: When one window resized, siblings in same column/row resize proportionally
|
||||
- Storage: Use `rowHeights` and `colWidths` arrays per krate (future enhancement)
|
||||
|
||||
### Window Grid
|
||||
- **Column pitch**: 412px
|
||||
- **Row pitch**: 416px
|
||||
- **Window default**: 380×362px (leaves room for grip/padding)
|
||||
|
||||
## Maximize Functionality
|
||||
|
||||
### Maximize State
|
||||
- Width: `viewport.width - 44px` (22px margins)
|
||||
- Height: `viewport.height - topBarHeight(68px) - bottomBarHeight(66px)`
|
||||
- Camera adjusted: `camX = 22 - krate.wx - window.dx`, `camY = 68 - krate.wy - window.dy`, `zoom = 1`
|
||||
|
||||
### Trigger
|
||||
- **Key**: `z` (when mouse is over window)
|
||||
- **Mouse**: Double-click header
|
||||
- Restore: Same trigger again
|
||||
|
||||
## Keyboard Focus Routing
|
||||
|
||||
### Mouse-Over Detection
|
||||
- Track `mouseenter`/`mouseleave` per window
|
||||
- Set `hoveredWindowId` ref
|
||||
- Clear on `mouseleave`
|
||||
|
||||
### Key Event Routing
|
||||
Global `keydown` handler with `{ capture: true }`:
|
||||
```
|
||||
if (hoveredWindowId is shell) {
|
||||
route keypress to shell
|
||||
} else if (hoveredWindowId is collection) {
|
||||
route keypress to filter input
|
||||
} else {
|
||||
let it fall through to canvas shortcuts
|
||||
}
|
||||
```
|
||||
|
||||
### Shell Special Case
|
||||
- Space character goes to terminal (NOT spotlight)
|
||||
- Must distinguish between canvas space-pan and shell space-input
|
||||
|
||||
## View-Specific Content
|
||||
|
||||
### YAML View
|
||||
- Display full object YAML
|
||||
- **Secret objects**: Base64-decode all data values
|
||||
- Show banner: `⊙ secret values auto-decoded`
|
||||
- Security: Handle carefully, never log decoded values
|
||||
|
||||
### Describe View
|
||||
- Human-readable object description
|
||||
- Formatted for readability
|
||||
- All metadata sections included
|
||||
|
||||
### Logs View
|
||||
- **Pods/workloads only**
|
||||
- **Live-tailing**: Stream via WebSocket
|
||||
- Auto-scroll to bottom on new lines (unless user scrolled up)
|
||||
- Color-code:
|
||||
- ERROR: `#ef6f6f`
|
||||
- WARN: `#e8b54a`
|
||||
- INFO: `#b9c6d8`
|
||||
|
||||
### Shell View
|
||||
- **Implementation**: xterm.js (real terminal)
|
||||
- **Backend**: WebSocket → `kubectl exec -it <pod> -n <ns> -- sh`
|
||||
- **Keyboard routing**: Route keypresses to terminal, passthrough terminal output
|
||||
- **Scroll wheel**: Scroll terminal content (NOT canvas pan)
|
||||
- **Ctrl/⌘+scroll**: Zoom canvas (not terminal zoom)
|
||||
|
||||
## Collection Window Specifics
|
||||
|
||||
### Size
|
||||
- Default: 540×480px (larger than detail windows)
|
||||
|
||||
### Header
|
||||
- Title: `pods · ns/payments`
|
||||
- Status summary badges: `✓ N / ⚠ N / ✗ N`
|
||||
- Filter input (auto-focused on open)
|
||||
- List / Tree toggle buttons
|
||||
- Keyboard hint: `↑↓ ⌥L/S/D/Y`
|
||||
|
||||
### List Mode
|
||||
- Rows: shape glyph + name + health metric + type badge + view buttons + status dot
|
||||
- Selected row: `background: accentDim; border-left: 2px solid accent`
|
||||
- Hover row: `background: rgba(255,255,255,.04)`
|
||||
- Sorted: degraded/failed first, then pending, then ready; alpha within groups
|
||||
|
||||
### Tree Mode (k9s xray)
|
||||
- Workloads expand to show pods
|
||||
- Pods expand to show configMaps, secrets, PVCs
|
||||
- Services/ingresses expand to show targets
|
||||
- Relation tags: `pod`, `configMap`, `secret`, `volume`, `selects`, `routes`, `used by`
|
||||
- Indent: `20px * depth`
|
||||
- Filtering: Keep matching nodes + all ancestors for hierarchy
|
||||
|
||||
## Window State
|
||||
```typescript
|
||||
interface Window {
|
||||
wid: string; // unique window ID
|
||||
kind: 'detail' | 'collection';
|
||||
tab: 'logs' | 'shell' | 'describe' | 'yaml';
|
||||
dx: number; // offset within krate (column * 412)
|
||||
dy: number; // offset within krate (row * 416)
|
||||
w: number; // width
|
||||
h: number; // height
|
||||
isMaximized: boolean; // current state
|
||||
prevW?: number; // previous width (for restore)
|
||||
prevH?: number; // previous height (for restore)
|
||||
}
|
||||
```
|
||||
|
||||
## Gotchas
|
||||
1. **Space key**: Must distinguish between canvas space-pan (capture with `{ capture: true }`) and shell space-input
|
||||
2. **Ctrl/⌘+scroll**: Use `{ capture: true }` on canvas to intercept before window scroll containers
|
||||
3. **Secret decoding**: Do server-side or careful client-side; never log decoded values
|
||||
4. **Window resizing**: Proportional siblings requires storing `rowHeights`/`colWidths` arrays (not just individual `dx/dy`)
|
||||
5. **Focus routing**: Mouse-over + type should focus shell/filter, not trigger canvas shortcuts
|
||||
6. **Collection auto-focus**: On creation, focus filter input in next tick using ref + `useEffect`
|
||||
Reference in New Issue
Block a user