3.9 KiB
3.9 KiB
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.
Core Responsibilities
- Render an infinite 2D workspace (12000×8000px world)
- Handle camera navigation (pan/zoom)
- Manage Level of Detail (LOD) based on zoom level
- Display a CAD grid overlay
- Coordinate global interactions (space panning, spotlight trigger)
Technical Requirements
Viewport & World Layer
- Full viewport (
position: fixed; inset: 0) - Background color:
#0b0e13 - World layer: 12000×8000px div transformed via
translate(camX, camY) scale(zoom) - All krates/windows are absolutely positioned children
Grid Overlay (CSS background-image)
- Fine grid: 34px spacing, rgba(125,145,175,.04)
- Coarse grid: 170px spacing, rgba(125,145,175,.075)
- Both X and Y repeating linear gradients
Camera State
interface CanvasState {
camX: number; // world-to-screen X offset
camY: number; // world-to-screen Y offset
zoom: number; // 0.16 – 2.2; default 0.92
flying: boolean; // true during animated camera transitions
dragging: boolean; // true during pan drag
collapsed: boolean; // LOD: true when zoom < 0.4
spacePanning: boolean; // true while space is held
}
Navigation
Scroll Wheel
- 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
- Implementation:
document.addEventListener('keydown', handler, { capture: true })on space- Set "panning" flag, prevent default on body
- Forward
mousemoveto canvas pan handler - Release on
keyupfor space - 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
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)
Animations
- CSS
transformwithtransitiononly for programmatic "fly" animations:.52s cubic-bezier(.22,.8,.28,1) - During user scroll/drag:
transition: nonefor immediacy
UI Components
Top Bar
- Position:
absolute; top: 0; left: 0; right: 0; z-index: 10; height ~56px - Layout:
display: flex; align-items: center; padding: 13px 18px - Background: none (transparent)
- Left side: Logo pill, cluster pill, krate count pill
- Right side: Synced pill, admin button, roster avatars
Bottom Hint Bar
- Position:
absolute; right: 18px; bottom: 18px - Shows keyboard hints (status bar)
Zoom Pill
- Position:
absolute; left: 18px; bottom: 18px - Displays current zoom percentage
Minimap
- Position:
absolute; right: 18px; bottom: 64px - Size: 180×120px
- Shows all krates as small rectangles
- Viewport indicator rectangle showing current view frustum
- Click anywhere to fly camera to that world position
Interaction Summary
| Interaction | Behavior |
|---|---|
| Scroll wheel | Pan canvas |
| Ctrl/⌘ + scroll | Zoom canvas |
| Space + drag | Pan canvas (even over windows/shells) |
| Click empty canvas | Open spotlight |
| Type any key | Open spotlight pre-seeded |
| Minimap click | Fly camera to position |
Gotchas
- Space pan over shells: Must intercept with
{ capture: true }, prevent default, but don't stop propagation - Ctrl/⌘+scroll: Use
{ capture: true }on canvas wheel handler to intercept before window scroll containers - Camera transitions: Only animate during programmatic fly, not user gestures
- LOD state: Use stable boolean
collapsedin state, not derived fromzoomon every render