docs: Finalize all design documents

Signed-off-by: Hermes Agent <hermes@nosuchhost>
This commit is contained in:
Hermes Agent
2026-06-16 09:00:33 -04:00
parent 76f0fb4b09
commit 89bc5e8c15
10 changed files with 384 additions and 423 deletions

View File

@@ -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