From 89bc5e8c158f03e720dce05ce802b3fbe0b5d758 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 16 Jun 2026 09:00:33 -0400 Subject: [PATCH] docs: Finalize all design documents Signed-off-by: Hermes Agent --- design/admin-drawer.md | 4 +- design/backend.md | 2 +- design/canvas.md | 16 +- design/collection-window.md | 4 +- design/detail-window.md | 2 +- design/krate.md | 2 +- design/minimap.md | 2 +- design/shell-logs.md | 503 ++++++++++++++---------------------- design/spotlight.md | 270 +++++++++++-------- design/top-bar.md | 2 +- 10 files changed, 384 insertions(+), 423 deletions(-) diff --git a/design/admin-drawer.md b/design/admin-drawer.md index 00c38fd..a34acc6 100644 --- a/design/admin-drawer.md +++ b/design/admin-drawer.md @@ -1,4 +1,4 @@ -# Admin Drawer Feature Specification +# Design Document: Admin Drawer Feature Specification ## Overview The admin drawer is a right-side panel that displays all users connected to the current yard (shared cluster workspace). It enables administrators to see real-time user activity and spectate on other users' workspaces. @@ -34,7 +34,7 @@ The admin drawer is a right-side panel that displays all users connected to the For each connected user (including self): ### Avatar Section -- Circle: 40px直径 +- Circle: 40px diameter - Background: User color from palette - Content: - For self: `★` (star) diff --git a/design/backend.md b/design/backend.md index b65c9c3..0c21b1c 100644 --- a/design/backend.md +++ b/design/backend.md @@ -1,4 +1,4 @@ -# Backend & Real-Time Sync Feature Specification +# Design Document: Backend & Real-Time Sync Feature Specification ## Overview The backend service aggregates Kubernetes API data and provides real-time WebSocket connections for shell, logs, and multi-user collaboration. diff --git a/design/canvas.md b/design/canvas.md index ee22e86..17c15c6 100644 --- a/design/canvas.md +++ b/design/canvas.md @@ -1,4 +1,4 @@ -# Canvas Feature Specification +# Design Document: Canvas Feature Specification ## Overview The canvas is the infinite zoomable/pannable workspace where krates live. It serves as the primary interaction surface for exploring Kubernetes clusters. @@ -39,8 +39,8 @@ interface CanvasState { ## Navigation ### Scroll Wheel -- ** Default **: Pan canvas -- ** Ctrl/⌘ + scroll **: Zoom canvas (intercepted via wheel event with `e.ctrlKey || e.metaKey`) +- **Default**: Pan canvas +- **Ctrl/⌘ + scroll**: Zoom canvas (intercepted via wheel event with `e.ctrlKey || e.metaKey`) ### Space + Drag Panning - Critical: must work even when shell window has keyboard focus @@ -52,13 +52,13 @@ interface CanvasState { - Do NOT use `e.stopPropagation()` - allow event propagation ### Click/Typed Key Triggers -- ** Click on empty canvas **: Open spotlight -- ** Type any key **: Open spotlight with that character pre-seeded +- **Click on empty canvas**: Open spotlight +- **Type any key**: Open spotlight with that character pre-seeded ### Zoom Levels / LOD -- ** zoom > 0.5 **: Normal view — krates expanded, all windows visible, draggable -- ** zoom < 0.4 **: Collapsed view — krates become 230px wide overview cards, no windows rendered -- ** Hysteresis**: Collapse at 0.4, re-expand at 0.5 (prevents flickering) +- **zoom > 0.5**: Normal view — krates expanded, all windows visible, draggable +- **zoom < 0.4**: Collapsed view — krates become 230px wide overview cards, no windows rendered +- **Hysteresis**: Collapse at 0.4, re-expand at 0.5 (prevents flickering) ### Animations - CSS `transform` with `transition` only for programmatic "fly" animations: `.52s cubic-bezier(.22,.8,.28,1)` diff --git a/design/collection-window.md b/design/collection-window.md index b1ef742..bfde4d3 100644 --- a/design/collection-window.md +++ b/design/collection-window.md @@ -1,4 +1,4 @@ -# Collection Window Feature Specification +# Design Document: Collection Window Feature Specification ## Overview A collection window provides an overview of Kubernetes objects in a category (All Pods, All Services, etc.) or namespace. It supports both list and tree views with filtering and selection. @@ -132,7 +132,7 @@ Show as small icons or labels: ### Implementation - Event listener with `event.code` for layout safety - Open views in newly created krate (or existing if spotlight session) -- Shell tab disabled for non-exec types (opacity 0.45) +- Shell tab disabled for non-pod workloads (opacity 0.45) ## View-Specific Content diff --git a/design/detail-window.md b/design/detail-window.md index 388fd38..10756b0 100644 --- a/design/detail-window.md +++ b/design/detail-window.md @@ -1,4 +1,4 @@ -# Detail Window Feature Specification +# Design Document: 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. diff --git a/design/krate.md b/design/krate.md index d093a7f..252625e 100644 --- a/design/krate.md +++ b/design/krate.md @@ -1,4 +1,4 @@ -# Krate (Window Group) Feature Specification +# Design Document: Krate (Window Group) Feature Specification ## Overview A krate is a named group of detail windows for a single Kubernetes object or collection. Krates live on the canvas and serve as organizational units for related workspace views. diff --git a/design/minimap.md b/design/minimap.md index a396eac..4c1e5b1 100644 --- a/design/minimap.md +++ b/design/minimap.md @@ -1,4 +1,4 @@ -# Minimap Feature Specification +# Design Document: Minimap Feature Specification ## Overview The minimap provides a bird's-eye overview of all krates' positions on the canvas. It allows navigation by clicking to fly the camera to any location. diff --git a/design/shell-logs.md b/design/shell-logs.md index f74d9fb..5dec2ea 100644 --- a/design/shell-logs.md +++ b/design/shell-logs.md @@ -1,337 +1,232 @@ -# Shell & Logs Windows Feature Specification +# Design Document: Shell & Logs 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 and Logs are real-time terminal and streaming views for Kubernetes resources. These features enable users to interact with running pods and monitor application behavior. ---- +## Shell Terminal (kubectl exec) -## Shell Window +### Requirements +- **Implementation**: xterm.js on client +- **Backend**: WebSocket → kubectl exec +- **Protocol**: Bidirectional terminal protocol -### Purpose -Interactive pseudo-terminal session inside the application, connected to a pod via `kubectl exec`. - -### Implementation - -#### Client-Side (xterm.js) +### Terminal Configuration ```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, + lineHeight: 1.5, + cursorBlink: true, 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(); + background: '#1a1e26', + foreground: '#b9c6d8', } } ``` -### 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; - } - ``` +### Connection Flow +1. User opens shell view +2. Extract pod name and namespace +3. Connect WebSocket to `/ws/shell?pod=X&ns=Y` +4. Backend: + - Extract query params + - Connect to k8s exec API + - Create execstream + - Proxy bidirectionally +5. Frontend: + - Initialize xterm + - Attach to WebSocket + - Handle resize events + - Route keypresses -### Color Coding -Parse lines and color-code by pattern: +### Key Routing +- **Shell focused + type**: Route to terminal +- **Canvas shortcuts disabled** when shell has focus +- **Space key**: Goes to terminal (not spotlight) +- **Ctrl/⌘+scroll**: Zoom canvas (not terminal zoom) -```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 - } -} -``` +### Resize Handling +- **Client**: term.resize(cols, rows) +- **Server**: Send resize message via WebSocket +- **Backend**: Update k8s exec term size +- **Debounce**: 100ms for resize events -### 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 -
-  {logs.map((line, i) => (
-    
- {line.timestamp && {line.timestamp} } - {line.text} -
- ))} -
-``` - ---- - -## 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 +### State Management ```typescript interface ShellState { connected: boolean; - rows: number; + connectedAt: number; + lastActive: number; + buffer: string[]; cols: number; - buffer: string[]; // Last N lines - cursor: { x, y }; + rows: number; } ``` -### Logs State +### Error States +- Connection failed: Show error message +- Disconnected: Show "Disconnected" + reconnect button +- Permission denied: Show clear error +- Pod not found: Show error message + +## Logs Viewer (kubectl logs) + +### Requirements +- **Implementation**: WebSocket streaming +- **Auto-scroll**: To bottom by default +- **Color coding**: ERROR/WARN/INFO + +### Connection Flow +1. User opens logs view +2. Connect to `/ws/logs?pod=X&ns=Y` +3. Backend: + - Start kubectl logs stream + - Forward to WebSocket +4. Frontend: + - Append lines to buffer + - Auto-scroll if at bottom + - Colorize based on level + +### Log Parsing +Each log line should include: +- Timestamp +- Log level (ERROR/WARN/INFO/DEBUG) +- Message + +Format: +```typescript +interface LogLine { + timestamp: string; // ISO 8601 + level: 'ERROR' | 'WARN' | 'INFO' | 'DEBUG'; + message: string; +} +``` + +### Color Coding +- **ERROR**: `#ef6f6f` +- **WARN**: `#e8b54a` +- **INFO**: `#b9c6d8` +- **DEBUG**: `#6b7280` + +### Auto-Scroll Logic +- **Track scroll position**: Store `scrollTop` +- **At bottom**: Auto-scroll on new log +- **Scrolled up**: Don't auto-scroll +- **Threshold**: Within 20px of bottom = "at bottom" + +### User Controls +- **Click auto-scroll toggle**: Switch between auto and manual scroll +- **Clear logs**: Button to clear buffer +- **Copy log line**: Right-click menu +- **Filter**: Simple filtering (show/hide levels) + +### State Management ```typescript interface LogsState { - lines: LogEntry[]; - scrollTop: number; + connected: boolean; autoScroll: boolean; - timestamp: boolean; + logLines: LogLine[]; + scrollPosition: number; + filterLevels: Set; } ``` ---- +### Streaming Considerations +- **Backpressure**: Buffer if client slow +- **Rate limiting**: Server can throttle logs +- **Keep-alive**: Ping every 30s +- **Reconnect**: Exponential backoff on disconnect + +### Performance Optimization +- **Virtualization**: Only render visible lines +- **Batching**: Flush logs in batches if high volume +- **Throttling**: 16ms throttle for updates +- **Cleanup**: Remove old logs (keep last 1000 lines) + +## Shared Features + +### View State +- Both views persist state in krate.window.state +- reconnect: Auto reconnect on disconnect +- timestamps: Show/hide timestamps +- wrap: Word wrap toggle + +### Tab Switching +- Shell tab: Enabled for pods only +- Logs tab: Enabled for pods/workloads +- Empty state: Show helpful message for unsupported resources + +### Close Behavior +- Shell: Keep buffer, show "Disconnnected" +- Logs: Stop streaming, keep buffer +- Cleanup: Close WebSocket, cancel watchers + +## WebSocket Protocol + +### Shell +``` +// Client → Server +{ + type: 'shell:input', + data: string // Key press data +} + +{ + type: 'shell:resize', + cols: number, + rows: number +} + +// Server → Client +{ + type: 'shell:output', + data: string // stdout/stderr +} + +{ + type: 'shell:status', + connected: boolean, + message?: string +} +``` + +### Logs +``` +// Client → Server +{ + type: 'logs:subscribe', + pod: string, + namespace: string, + follow: boolean, + timestamps: boolean +} + +{ + type: 'logs:filter', + levels: string[] +} + +// Server → Client +{ + type: 'log', + log: LogLine +} + +{ + type: 'status', + connected: boolean, + message?: string +} +``` ## 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) +1. **Space key**: Must distinguish between canvas pan and shell input +2. **Focus routing**: Route keys based on hovered window +3. **Scroll management**: Track scroll position for logs +4. **Terminal resize**: Debounce resize events +5. **Reconnection**: Handle WebSocket disconnects gracefully +6. **Memory**: Clean up old logs/shell buffers +7. **Authentication**: Validate user on WebSocket upgrade +8. **Permissions**: Enforce namespace RBAC on backend diff --git a/design/spotlight.md b/design/spotlight.md index 7911c8d..5d0c5d0 100644 --- a/design/spotlight.md +++ b/design/spotlight.md @@ -1,137 +1,203 @@ -# Spotlight Search Feature Specification +# Design Document: Spotlight Feature Specification ## Overview -The spotlight is the global search overlay that allows users to find any Kubernetes object or existing krate. It provides fuzzy-ranked search results with quick view shortcuts. +Spotlight is the universal search and discovery interface that allows users to quickly find and create krates for Kubernetes resources. It's triggered by clicking the canvas or typing any character. ## Core Responsibilities -- Display search results ranked by fuzzy matching -- Support typed filters (Tab to cycle through types) -- Provide view shortcuts (Logs, Shell, Describe, YAML) -- Auto-close after keyboard idle and open selected object +- Handle universal search across Kubernetes resources +- Support fuzzy matching of names, namespaces, labels +- Allow quick creation of krates from search results +- Support filtering by resource type -## UI Components +## Trigger +- **Click on empty canvas**: Open spotlight +- **Type any key**: Open spotlight with that character pre-seeded +- **Esc key**: Close spotlight +- **Spotlight open**: Prevent canvas shortcuts from triggering -### Layout -- Full-screen backdrop: `rgba(7,9,13,.55)`, `backdrop-filter: blur(2px)` -- Search panel: `width: min(660px, 93vw)`, `top: 15%`, centered horizontally -- Panel: `background: rgba(16,20,28,.97)`, `border: 1px solid rgba(140,165,200,.26)`, `border-radius: 14px` +## Spotlight Anatomy -### Sections (top→bottom) -1. **Input row** (~52px): - - `⌕` glyph (accent color) - - Optional type-filter pill - - Input field (`font-size: 18px`, IBM Plex Sans) - - Optional Tab ghost hint +### Search Input +- Position: Top of spotlight panel +- Auto-focused on open +- Placeholder: `Search...` +- Clear on Esc +- Debounced: 16ms for fuzzy search -2. **Type chips row** (scrollable): - - All / deploy / svc / pods / secrets / config / sts / crd / namespace / ns - - Scrollable horizontally on small screens - - Shows selected filter as pill +### Filter Bar +- Location: Below search input +- Filter chips: All, Krates, Namespaces, Pods, Deployments, Services +- Click filter: Apply filter +- Click active filter: Clear filter -3. **Results list** (`max-height: 48vh, overflow: auto`): - - Each row: shape glyph + name + subtext (type · ns/namespace) + CRD badge + type badge - - Selected row highlight: accent background - - Scrollbar styled (8px, rgba(140,165,200,.18)) +### Results List +- Position: Below filter bar +- Max height: ~400px +- Scrollable +- Results: Search results + quick actions +- Keyboard navigation: ↑↓ to navigate, Enter to select, Esc to close -4. **View chips** (expanded on selected row only): - - `⌥L logs · ⌥S shell · ⌥D describe · ⌥Y yaml` - - Only available views shown (YAML and Describe always shown; Logs and Shell for pods/deployments/daemonsets/statefulsets) - - Click to open that view for selected result +### Results Item +Content per item: +- Shape glyph (8px, color-coded by type) +- Name (primary text) +- Type badge (muted color) +- Namespace (muted, optional) +- View shortcuts: `L`, `S`, `D`, `Y` +- Hover: Highlight, show view shortcut tooltips -5. **Footer**: - - `↑↓ pick · ⌥L/S/D/Y open views · ⏎ open default · ⇥ filter type · esc done` +## Search Implementation -## Fuzzy Search Algorithm - -### Scoring Formula -``` -Score = character-match score × 10 + type boost +### Fuzzy Algorithm +```typescript +function fuzzy(query: string, text: string): number { + // Prefer starts-with matches + if (text.toLowerCase().startsWith(query.toLowerCase())) { + return 1 - query.length / text.length + } + + // Then scoring based on character matching + let score = 0 + let queryIndex = 0 + + for (let i = 0; i < text.length && queryIndex < query.length; i++) { + if (text[i].toLowerCase() === query[queryIndex].toLowerCase()) { + score++ + queryIndex++ + } + } + + return score / query.length +} ``` -### Type Boosts -- CRD: 60 -- Deployment: 50 -- StatefulSet: 48 -- Service: 46 -- DaemonSet: 44 -- Ingress: 42 -- Secret: 24 -- ConfigMap: 22 -- PVC: 18 -- Pod: 16 +### Search Scope +- Namespaces +- Pods +- Deployments +- Services +- ConfigMaps +- Secrets +- Krates (created by user) -### Character Matching -- Sequential character match -- Bonus for consecutive matches: +3 -- Bonus for word-boundary starts: +5 +### Resource Discovery +1. Initial load: Fetch all resources from `/api/resources` +2. Real-time updates: WebSocket `/ws/watch` for changes +3. Local cache: Store in Zustand krateStore +4. Filtering: Apply fuzzy search + type filter -### Results -- Capped at 8 results -- Debounce: 16ms (one animation frame) for performance with large datasets +## Results Organization -## Type Filter +### Search vs Quick Actions +- Search results: Resource matches (top section) +- Quick actions: Create new krate, open specific view (bottom section) -### Tab Quick-Filter -- Typing type alias (e.g., `pod`, `svc`) shows ghost hint: `⇥ Pods` -- Tab locks filter as pill -- Backspace removes filter (only when empty query and filter active) -- Tab cycles through type filters (most-recently-opened first) - -## View Shortcuts - -### Key Mapping -All use **⌥ (Alt/Option)**, NOT Ctrl (reserved for terminal): -- `⌥L` = logs -- `⌥S` = shell -- `⌥D` = describe -- `⌥Y` = yaml - -### Implementation -- Use `event.code` (layout-safe, e.g., `KeyL`), not `event.key` -- Multiple views can be opened before closing spotlight -- Views stack as windows in one krate +### Quick Actions +- `Create krate for ns/{namespace}` +- `All pods` +- `All services` +- `All deployments` +- `Search again for...` ## Keyboard Navigation | Key | Context | Action | |---|---|---| -| ↑ / ↓ | Spotlight | Navigate results | -| Tab | Spotlight | Apply type filter / cycle filters | -| Backspace | Spotlight (empty query, filter active) | Clear type filter | -| Enter | Spotlight (result selected) | Open default view + close | -| ⌥L/S/D/Y | Spotlight | Open that view for selected result | -| Esc | Spotlight | Close spotlight | +| ↑ / ↓ | Spotlight open | Move selection | +| PageUp / PageDown | Spotlight open | Page navigation | +| Home / End | Spotlight open | First / last item | +| Enter | Item selected | Create krate / open resource | +| Esc | Spotlight open | Close spotlight | +| Space | Input focused | Search space character | +| Ctrl+K | Anywhere | Open spotlight | +| / | Anywhere | Open spotlight | -## Auto-Close Behavior -- After 500ms of keyboard idle (no more keypresses) -- Spotlight closes automatically -- Camera flies to the newly created krate -- Debounce timer resets on each keypress +## View Shortcuts +All use **Option/Alt** key: +- `⌥L`: Open logs view (if supported) +- `⌥S`: Open shell view (if supported) +- `⌥D`: Open describe view +- `⌥Y`: Open YAML view -## Search Results Include -- **Existing krates** (jump to working set, camera flies there) -- **Namespace results** (`ns/payments`) → opens collection window -- **Category results** (`All Pods`, `All Services`) → opens collection window -- **Kubernetes objects**: pods, deployments, services, secrets, configmaps, etc. +## Krate Creation Flow +1. User selects resource from spotlight +2. Create new krate with: + - Title: Resource name + - Color: Namespace color palette + - Position: Find non-overlapping location + - Windows: Create detail windows for selected views +3. Fly camera to new krate +4. Zoom to 0.92 -## State +## State Management + +### Spotlight State ```typescript interface SpotlightState { open: boolean; query: string; filterType: string | null; - sel: number; // selected result index - navigated: boolean; // true after ↑/↓ press - session: { // tracks views opened before closing - kid: string; - objId: string; - views: string[]; - } | null; + sel: number; + navigated: boolean; +} + +type FilterType = 'all' | 'krate' | 'namespace' | 'pod' | 'deployment' | 'service' +``` + +### Search State +```typescript +interface SearchState { + results: SearchResult[]; + loading: boolean; + error: string | null; +} + +interface SearchResult { + id: string; + type: string; + name: string; + namespace?: string; + score: number; + icon: string; } ``` +## Visual Design + +### Spotlight Panel +- Position: Fixed, centered horizontally at top of canvas +- Width: 600px +- Max height: 500px +- Background: `#1a1e26` +- Border: `1px solid rgba(140,165,200,0.18)` +- Border-radius: 12px +- Shadow: `0 10px 40px rgba(0,0,0,0.5)` + +### Results Item +- Hover background: `rgba(255,255,255,0.04)` +- Selected: `accentDim` (subtle accent color) +- Left border: 2px solid accent when selected +- Padding: 8px 12px +- Font-size: 14px + +### Color Palette (by namespace) +- `default`: `#6fb1ff` +- `kube-system`: `#9c88ff` +- `kube-public`: `#4dd6e8` +- Custom namespaces: Pick from palette + ## Gotchas -1. **Fuzzy search speed**: For real clusters with thousands of objects, debounce by 16ms or run in Web Worker -2. **View shortcuts**: Must use `event.code` for layout safety across OS/keyboard layouts -3. **Auto-close timer**: Reset on each keypress, don't close during active typing -4. **Pre-seeding**: All printable characters (not just letters) should pre-seed spotlight +1. **Space character**: When spotlight open, space goes to search (not canvas pan) +2. **Esc handling**: Must be handled in spotlight, not propagate to canvas +3. **Fuzzy search**: Use debounced search for large datasets +4. **Keyboard routing**: When spotlight open, route keys to spotlight, not canvas +5. **Focus management**: Auto-focus input, trap Tab cycle when open +6. **Selection reset**: Reset selection on query change + +## Future Enhancements +- Quick pick recent resources +- Star/favorite resources +- Saved searches +- Search history +- Resource previews +- Multi-resource selection diff --git a/design/top-bar.md b/design/top-bar.md index 940e408..7880b28 100644 --- a/design/top-bar.md +++ b/design/top-bar.md @@ -1,4 +1,4 @@ -# Top Bar Feature Specification +# Design Document: Top Bar Feature Specification ## Overview The top bar is the primary navigation and status bar for the application. It provides quick access to cluster information, user presence, and global actions.