docs: Finalize all design documents
Signed-off-by: Hermes Agent <hermes@nosuchhost>
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user