From 76f0fb4b090a99499e0d87251f96d25b027dde4a Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 16 Jun 2026 08:53:57 -0400 Subject: [PATCH] docs: Add comprehensive README --- README.md | 699 +++++++++++------------------------------------------- 1 file changed, 143 insertions(+), 556 deletions(-) diff --git a/README.md b/README.md index 45b6027..8a40a38 100644 --- a/README.md +++ b/README.md @@ -1,567 +1,154 @@ -# Handoff: Krates — Kubernetes Visual Cluster Navigator +# Krates - Kubernetes Workspace Explorer -## Overview +A collaborative, real-time Kubernetes workspace explorer with an infinite canvas interface. -Krates is a keyboard-first, search-driven Kubernetes cluster navigator. It gives operators and developers a fast, spatial way to explore any cluster: search for any object, open exactly the views you need (logs, shell, describe, YAML), and keep them organized as named **krates** on an infinite zoomable canvas. Multiple users share the same cluster workspace (the **yard**), and an admin view shows all users' live queries and open krates. +## Architecture -The product lives at **krates.io**. +### Frontend +- **Framework**: React 18 + TypeScript +- **Styling**: Inline styles (CSS-in-JS) +- **Terminal**: xterm.js +- **Real-Time**: Yjs + y-websocket (CRDT collaboration) +- **Build Tool**: Vite +- **State**: Zustand ---- +### Backend +- **Language**: Go +- **Kubernetes Client**: kubernetes/client-go +- **WebSockets**: gorilla/websocket +- **CRDT**: Yjs + y-websocket -## About the Design Files - -`Krates.dc.html` is a **high-fidelity interactive prototype** — it runs in a browser and is fully interactive. It is **not production code**. Its purpose is to demonstrate the intended look, behavior, keyboard model, and data shapes precisely enough that a developer can implement the real app without guessing. - -**Your task:** Recreate this UI in a real production stack (recommended: React + TypeScript + a WebSocket/CRDT backend for real-time sync). Use the prototype as the authoritative reference for interactions, visual design, and UX decisions. - ---- - -## Fidelity - -**High-fidelity.** Colors, typography, spacing, animations, and interaction behavior are all final. Implement pixel-accurately. The prototype uses real mock cluster data; the real app will use a Kubernetes API server (kubectl proxy, or a Go/Rust backend aggregating the k8s API). - ---- - -## Core Mental Model - -| Term | Meaning | -|---|---| -| **Yard** | The shared cluster workspace — one per connected cluster | -| **Krate** | A named group of windows for one object (or a collection); sits on the canvas | -| **Window** | A single view (Logs / Shell / Describe / YAML) for one object, inside a krate | -| **Collection window** | A category or namespace overview (all Pods, all Services, ns/payments…) with a filterable list | -| **Spotlight** | The global search overlay — opens by clicking anywhere or typing any key | - ---- - -## Screens / Views - -### 1. Canvas (main surface) - -**Purpose:** Infinite zoomable/pannable workspace where krates live. The default starting point is empty — no pre-placed content. - -**Layout:** -- Full viewport, `position: fixed; inset: 0` -- Background: `#0b0e13` -- CAD grid overlay: two layers — fine (`34px`, rgba(125,145,175,.04)) and coarse (`170px`, rgba(125,145,175,.075)), both in X and Y. Rendered via CSS `background-image` repeating linear gradients. -- World layer: a large div (`12000×8000px`) transformed via `translate(camX, camY) scale(zoom)`. All krates and windows are absolutely positioned children of this layer. -- Top bar: `position: absolute; top: 0; left: 0; right: 0; height ~56px; z-index: 10` -- Bottom hint bar: `position: absolute; right: 18px; bottom: 18px` -- Zoom pill: `position: absolute; left: 18px; bottom: 18px` -- Minimap: `position: absolute; right: 18px; bottom: 64px; width: 180px; height: 120px` - -**Navigation:** -- **Scroll wheel** = pan (default) -- **Ctrl/⌘ + scroll** = zoom (intercepted via `wheel` event, `e.ctrlKey || e.metaKey`) -- **Space + drag** = pan. Critical: space panning must work even when a shell window has keyboard focus. Implement by capturing `keydown` for space globally with `capture: true`, setting a "panning" flag, preventing default on the body, and forwarding `mousemove` to the canvas pan handler. Release on `keyup` for space. -- **Click on empty canvas** = open spotlight -- **Type any key** = open spotlight with that character pre-seeded - -**Zoom levels / LOD (Level of Detail):** -- `zoom > 0.5`: normal view — krates expanded, all windows visible, draggable -- `zoom < 0.4`: collapsed view — krates become small overview cards (230px wide), no windows rendered. **Hysteresis:** collapse at 0.4, re-expand at 0.5 to prevent flickering during a single scroll gesture. -- The camera uses CSS `transform` with a `transition` only during programmatic "fly" animations (`.52s cubic-bezier(.22,.8,.28,1)`). During user scroll/drag, transition is `none` for immediacy. - -### 2. Top Bar - -**Layout:** `display: flex; align-items: center; padding: 13px 18px` -**Background:** none (transparent over canvas) - -Left side (left→right): -- **Logo pill**: `krates / yard` — `background: rgba(14,18,25,.82); border: 1px solid rgba(140,165,200,.18); border-radius: 9px; padding: 7px 12px; backdrop-filter: blur(6px)`. The diamond glyph is `clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%)` in accent color. -- **Cluster pill**: cluster name + green health dot (`#4ad07a`, `box-shadow: 0 0 8px #4ad07a`) -- **Krate count pill**: only shown when krates exist - -Right side: -- **Synced pill**: pulsing accent dot + "synced" text -- **Admin button**: `◉ admin` — accent-outlined when active, muted when inactive -- **Roster avatars**: overlapping circles (`margin-left: -7px`), each 30×30px, colored per user, showing 2-letter initials (★ for self) - -### 3. Spotlight Search - -**Purpose:** Global search overlay. Fuzzy-ranked results, Tab type-filter, view shortcuts on selected result. - -**Trigger:** Click anywhere on empty canvas, or type any printable character (pre-seeds the query). - -**Layout:** -- Full-screen backdrop: `rgba(7,9,13,.55)`, `backdrop-filter: blur(2px)` -- Search panel: `position: absolute; left: 50%; top: 15%; transform: translateX(-50%); width: min(660px, 93vw)` -- Panel: `background: rgba(16,20,28,.97); border: 1px solid rgba(140,165,200,.26); border-radius: 14px; overflow: hidden` -- Input row height: ~52px, with `font-size: 18px`, IBM Plex Sans - -**Sections (top→bottom):** -1. **Input row**: `⌕` glyph (accent color) + optional type-filter pill + input field + optional Tab ghost hint -2. **Type chips row**: scrollable chip row for all/deploy/svc/pods/secrets/config/sts/crd/namespace/ns. Scrollable horizontally on small screens. -3. **Results list** (`max-height: 48vh, overflow: auto`): each row = shape glyph + name + subtext (type · ns/namespace) + CRD badge if applicable + type badge -4. **View chips** (expanded on the selected row only): `⌥L logs · ⌥S shell · ⌥D describe · ⌥Y yaml`. Only available views shown — e.g. YAML and Describe are always shown; Logs and Shell only for pods/deployments/daemonsets/statefulsets. -5. **Footer**: `↑↓ pick · ⌥L/S/D/Y open views · ⏎ open default · ⇥ filter type · esc done` - -**Fuzzy search algorithm:** -- Score = character-match score × 10 + type boost -- Type boost: crd=60, deployment=50, statefulset=48, service=46, daemonset=44, ingress=42, secret=24, configmap=22, pvc=18, pod=16 -- Character matching: sequential character match with bonuses for consecutive matches (+3) and word-boundary starts (+5) -- Results capped at 8 - -**Tab quick-filter:** -- Typing a type alias (`pod`, `svc`, `deploy`, `secret`, `cm`, `sts`…) shows a ghost hint: `⇥ Pods` -- Tab locks the filter as a pill; Backspace removes it -- Tab also **cycles** through all type filters (recency-ordered, most-recently-opened first) - -**View shortcut keys:** -- All use **⌥ (Alt/Option)**. Do NOT use Ctrl — it must stay free for terminal use. -- Keys: `Alt+L` = logs, `Alt+S` = shell, `Alt+D` = describe, `Alt+Y` = yaml -- Use `event.code` (layout-safe, e.g. `KeyL`) not `event.key` (which varies by OS/keyboard layout) -- Multiple views can be opened before closing spotlight — they stack as windows in one krate -- On Enter: opens the default view (logs for pods, describe for workloads, yaml for config/secrets/pvcs) and closes - -**Auto-close:** after 500ms of keyboard idle (no more keypresses), spotlight closes automatically and the camera flies to the newly created krate. - -**Search also returns:** -- Existing krates (jump to working set, camera flies there) -- Namespace results (`ns/payments`) → opens a collection window -- Category results (`All Pods`, `All Services`…) → opens a collection window - -### 4. Krate (window group) - -**Purpose:** A named group of detail windows for a single query/object. Lives on the canvas. - -**Visual container:** -- Dashed border frame (rounded rect) around all its windows: `border: 1px dashed rgba(color, .3); border-radius: 18px; background: rgba(color, .04)` -- Frame auto-fits to the bounding box of its windows + 30px padding -- Header label bar above the frame: shows object name, status badge, window count, **drag handle** to move the entire krate, **×** to dismiss - -**Layout of windows inside a krate:** -- Windows are tiled in a 2-column grid (col 0 = dx:0, col 1 = dx:412; row height = 416px) -- When one window is resized, the others resize proportionally within the grid column/row -- Windows never overlap (snap-to-grid on release) - -**Krate interactions:** -- **Drag header** = move entire krate -- **Double-click header** = collapse/expand (toggle) -- **Minimize button** (`—`) in header = collapse to overview card -- **Collapsed state**: 230px-wide card showing name, view-letter badges (Y/D/L/S), status dot, window count, ns. **Double-click to re-expand and fly camera to it.** -- **Non-overlapping placement:** on mouse release after drag, check all other krate bounding boxes and nudge to avoid overlap. Gentle snap, not strict grid. - -**Camera fly-to on open:** after summon, camera animates to center the krate at `zoom: 0.92`. - -### 5. Detail Window - -**Purpose:** Shows one view (YAML / Describe / Logs / Shell) for one Kubernetes object. - -**Size:** Default 380×362px. Resizable by dragging the bottom-right corner handle (resize one window in a krate → sibling windows resize proportionally in the same column/row). - -**Header (36px):** -- Shape glyph (8px, color-coded by object type) + view label (uppercase, accent-colored) + object name + status badge + **×** close -- `cursor: grab` — drag to reposition within the canvas -- **Double-click header** = zoom window to fill available space (below top bar, above bottom bar, side margins 22px). Press `z` or double-click again to restore. - -**Tab bar:** YAML · Describe · Logs · Shell. Shell tab is disabled (muted, opacity 0.45) for non-exec object types. - -**Content area:** `
` with `overflow: auto; height: 286px; font-family: 'IBM Plex Mono'; font-size: 12px; line-height: 1.65`. Scrollbar styled (8px, rgba(140,165,200,.18)).
-
-**Keyboard focus:**
-- **Mouse-over a window → start typing** = focuses that window's content (specifically the shell input if it's a shell view)
-- Mouse-over detection: track `mouseenter`/`mouseleave` per window, set a `hoveredWindowId` ref
-- In the global `keydown` capture handler: if `hoveredWindowId` refers to a shell, route the keypress there; if it's any other window, do nothing (let it fall through to canvas shortcuts)
-
-**Maximize (`z` key or double-click header):**
-- Resize the window to `width = viewport.width - 44px`, `height = viewport.height - topBarHeight(68px) - bottomBarHeight(66px)`
-- Set camera so `camX = 22 - krate.wx - window.dx`, `camY = 68 - krate.wy - window.dy`, `zoom = 1`
-- `z` again or double-click to restore previous size
-
-**Secret YAML auto-decode:**
-- When rendering YAML for a Secret object, base64-decode all data values and show plaintext with a `⊙ secret values auto-decoded` banner
-
-### 6. Shell Window
-
-**Purpose:** Interactive pseudo-terminal session inside the app.
-
-**Implementation:**
-- Use **xterm.js** (or equivalent) for the real terminal
-- Backend: WebSocket → kubectl exec into the pod
-- Keyboard routing: the window captures keypresses when the mouse is over it (see above). Space must NOT open the spotlight when the cursor is over a shell — instead pass the space character to the terminal.
-- Scroll wheel over shell: scroll the terminal content, do NOT pan the canvas. Only `Ctrl/⌘+scroll` over a shell should zoom the canvas.
-
-**Mock in prototype:** Shows static text with a blinking cursor character. The real implementation connects via `kubectl exec -it  -n  -- sh` through the backend.
-
-### 7. Logs Window
-
-**Purpose:** Live-tailing container logs.
-
-**Implementation:**
-- Streams via `kubectl logs --follow` through the backend WebSocket
-- Auto-scroll to bottom when new lines arrive, unless the user has scrolled up (detect by comparing `scrollTop + clientHeight` vs `scrollHeight`; if not at bottom, stop auto-scroll)
-- Color-code: ERROR lines in `#ef6f6f`, WARN in `#e8b54a`, INFO in `#b9c6d8`
-
-### 8. Collection Window
-
-**Purpose:** Overview of a category (All Pods, All Services…) or namespace. Status-at-a-glance list.
-
-**Size:** Default 540×480px.
-
-**Header:**
-- Title (e.g. "pods · ns/payments"), status summary badges (✓ N / ⚠ N / ✗ N)
-- **Filter input** (auto-focused on open): `font-family: IBM Plex Mono; font-size: 12px`
-- Keyboard hint: `↑↓ ⌥L/S/D/Y` (always visible, muted)
-- **List / Tree** toggle buttons
-
-**List mode:**
-- Each row: shape glyph + name + relation tag (muted, for tree mode) + health metric + type badge + view buttons (⌥L/S/D/Y) + 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 each group
-
-**Tree mode (k9s xray):**
-- Each workload expands to show its pods; each pod/workload expands to show its configMaps, secrets, PVCs
-- Services/ingresses expand to show their targets
-- Relation tags shown: `pod`, `configMap`, `secret`, `volume`, `selects`, `routes`, `used by`
-- Indent per level: `20px * depth`; connector shown as an L-shaped border element
-
-**Filter behavior in collection window:**
-- Typing filters immediately (no mode switching)
-- `↑/↓` moves selection highlight
-- `⌥L/S/D/Y` opens that view for the selected row (uses `event.code`, Alt required)
-- `Enter` opens default view for selected row
-- `Escape` blurs the input
-- Mouse-over the collection window + start typing = focuses the filter input and types there (same hover routing as shell windows, but for the filter input)
-- In tree mode, filtering keeps matching nodes **plus all their ancestors** so the hierarchy stays readable
-
-### 9. Admin Drawer
-
-**Purpose:** Right-side panel showing all users connected to the yard.
-
-**Trigger:** `◉ admin` button in top bar (toggle).
-
-**Layout:**
-- Right-side slide-in panel: `width: 380px; top: 0; right: 0; bottom: 0; position: absolute; z-index: 56`
-- Backdrop: `rgba(7,9,13,.4)`, click to close
-- Background: `rgba(13,17,24,.98); border-left: 1px solid rgba(140,165,200,.2)`
-
-**Content per user:**
-- Avatar circle + name + status (active · namespace / idle Nm)
-- Current search query + "typing…" pulsing indicator if they have spotlight open
-- Krate count
-- Each open krate as a clickable row: name + view-letter badges + status dot
-
-**Admin can spectate:** clicking a krate row in another user's card (or in "You") closes the admin panel and flies the camera to that krate.
-
-**Your own row** shows real-time state — your current spotlight query and actual open krates.
-
-### 10. Minimap
-
-**Purpose:** Overview of all krates' positions on the canvas.
-
-**Position:** `right: 18px; bottom: 64px`  
-**Size:** `180×120px`, with a viewport indicator rectangle showing the current view frustum.
-
-**Interaction:** Click anywhere on the minimap to fly the camera to that world position.
-
----
-
-## Interactions & Behavior Summary
-
-| Interaction | Behavior |
-|---|---|
-| Click empty canvas | Open spotlight |
-| Type any key (not in input) | Open spotlight pre-seeded with that key |
-| `/` key | Open spotlight empty |
-| `Esc` | Close spotlight / close spotlight and place krate |
-| Scroll wheel | Pan canvas |
-| Ctrl/⌘ + scroll | Zoom canvas |
-| Space + drag | Pan canvas (even over windows/shells) |
-| `z` over a window | Maximize window to fill available space |
-| `z` again | Restore window size |
-| Double-click window header | Maximize / restore |
-| Double-click krate header | Collapse / expand krate |
-| Double-click collapsed krate card | Expand and fly camera to it |
-| Mouse-over window + type | Focus shell / collection filter |
-| ⌥L / ⌥S / ⌥D / ⌥Y | Open view for selected result (spotlight or collection) |
-| Tab in spotlight | Apply type filter / cycle type filters |
-| Backspace in spotlight (empty query, filter active) | Clear type filter |
-| ↑↓ in spotlight | Navigate results |
-| ⌥+key in spotlight | Open view for selected result |
-| ↑↓ in collection filter | Move row selection |
-| ⌥+key in collection | Open view for selected row |
-| Drag window header | Move window |
-| Drag krate header | Move entire krate |
-| Krate drag release | Nudge to avoid overlap with other krates |
-| Resize window handle | Resize; siblings resize proportionally |
-| Minimap click | Fly camera to position |
-
----
-
-## State Management
-
-### Canvas state
-```typescript
-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
-}
-```
-
-### Krate state
-```typescript
-interface Krate {
-  id: string;
-  objId: string | null;        // null for collection krates
-  collScope?: { kind: 'namespace' | 'category', value: string };
-  label: string;
-  status: string;
-  color: string;               // hex, from namespace palette
-  wx: number; wy: number;      // world position
-  minimized: boolean;
-  windows: Window[];
-  seq: number;                 // for generating window IDs
-}
-
-interface Window {
-  wid: string;
-  kind: 'detail' | 'collection';
-  tab: 'logs' | 'shell' | 'describe' | 'yaml';
-  dx: number; dy: number;      // offset within krate
-  w: number; h: number;
-}
-```
-
-### 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;
-}
-```
-
-### Collection window state (per-window ID)
-```typescript
-interface CollectionState {
-  [wid: string]: {
-    search: string;
-    view: 'list' | 'tree';
-    sel: number;               // selected row index
-  }
-}
-```
-
----
-
-## Backend / API Requirements
-
-The prototype uses **mock data**. The real app needs:
-
-### Kubernetes data
-- **kubectl proxy** or a dedicated Go/Rust aggregation service
-- Watch API for live updates: `GET /api/v1/namespaces/{ns}/pods?watch=true` etc.
-- Resources needed: Namespaces, Deployments, StatefulSets, DaemonSets, Services, Ingresses, ConfigMaps, Secrets, PVCs, Events, and CRDs
-- Secret values should be base64-decoded server-side (or client-side) before display
-
-### Real-time shell
-- WebSocket → `kubectl exec -it` stream
-- Bidirectional: keypresses → stdin, stdout/stderr → display
-
-### Real-time logs
-- WebSocket → `kubectl logs --follow`
-- Stream new lines; handle reconnection
-
-### Multi-user sync (the Yard)
-- **CRDTs or operational transforms** for shared krate positions, krate existence, window layout
-- Recommended: **Yjs** (CRDT library) over WebSocket (y-websocket)
-- Each user's cursor/active query broadcast as ephemeral state (Yjs awareness)
-- Admin view reads from the awareness map
-
-### Recommended backend stack
-- **Go** service wrapping the Kubernetes client-go library
-- WebSocket multiplexer for shell, logs, watch streams
-- **Redis** or **in-memory** for session/presence if single-node
-- For multi-node: use Yjs with a persistent provider (y-leveldb or y-mongodb)
-
----
-
-## Design Tokens
-
-### Colors
-```
-Background:          #0b0e13
-World background:    #0b0e13
-Surface:             rgba(14,18,25,.97)
-Surface elevated:    rgba(20,26,36,.7)
-Border default:      rgba(140,165,200,.2)
-Border subtle:       rgba(140,165,200,.14)
-
-Accent (cyan):       #4dd6e8
-Accent (violet):     #a98cff
-Accent (amber):      #f5b454
-
-Text primary:        #e6edf6
-Text secondary:      #c7d2e0
-Text muted:          #9fb0c8
-Text faint:          #7e8aa2
-Text dimmed:         #6b7890
-
-Status OK:           #4ad07a
-Status warn:         #e8b54a
-Status error:        #ef6f6f
-
-Object colors:
-  Deployment:        #6fb1ff
-  StatefulSet:       #b89cff
-  Service:           #5fd0c0
-  DaemonSet:         #ffb27a
-  Ingress:           #e58fb0
-  CRD:               #ffd479
-  Pod:               #8aa0bd
-  ConfigMap:         #7fb39c
-  Secret:            #c79bd6
-  PVC:               #9aa7c7
-
-Namespace palettes:
-  payments:          #6fb1ff
-  platform:          #5fd0c0
-  storefront:        #e58fb0
-```
-
-### Shape language (CSS clip-path per object type)
-```
-Deployment:   border-radius: 50% (circle)
-StatefulSet:  polygon(50% 0, 100% 100%, 0 100%) (triangle)
-Service:      polygon(50% 0, 100% 50%, 50% 100%, 0 50%) (diamond)
-DaemonSet:    ring (transparent fill, colored border)
-Ingress:      polygon(50% 0, 100% 100%, 0 100%) (triangle, same as STS)
-CRD:          polygon(25% 0, 75% 0, 100% 50%, 75% 100%, 25% 100%, 0 50%) (hexagon)
-Pod:          circle
-ConfigMap:    square (border-radius: 3px)
-Secret:       hexagon (same clip-path as CRD, smaller)
-PVC:          square
-```
-
-### Typography
-```
-UI font:      IBM Plex Sans, 400/500/600
-Mono font:    IBM Plex Mono, 400/500
-
-Sizes used:
-  9px   — label/badge uppercase legends
-  10px  — tiny metadata, status labels
-  11px  — chips, hints, secondary metadata
-  12px  — mono body text in windows/collections
-  13px  — nav bar labels, search result sub-text
-  14px  — collection titles, admin panel
-  17px  — spotlight search input prefix glyph
-  18px  — spotlight search input
-  25px  — empty state headline
-```
-
-### Spacing
-```
-Window default size:   380 × 362px
-Collection default:    540 × 480px
-Window grid col gap:   412px (column pitch)
-Window grid row gap:   416px (row pitch)
-Krate frame padding:   30px around bounding box
-Top bar height:        ~56px (inset: 68px with padding)
-Bottom bar height:     ~40px (inset: 66px with padding)
-Minimap:               180 × 120px
-Sidebar margin:        18px
-```
-
-### Animations
-```
-Camera fly:       transform .52s cubic-bezier(.22,.8,.28,1)
-Spotlight open:   opacity + translateY(-8px→0) .16s cubic-bezier(.2,.8,.3,1)
-Window appear:    opacity + scale(.97→1) .16s ease
-Fade backdrop:    opacity .12s ease
-Krate logo float: translateY 0→-7px→0, 4s ease-in-out infinite
-Synced dot blink: opacity 50% at 50%, 1.6s ease-in-out infinite
-```
-
----
-
-## Keyboard Shortcut Reference
-
-All shortcuts that act on objects use **⌥ (Alt/Option)** — never Ctrl (reserved for terminal), never bare letters (reserved for typing in search/filter).
-
-| Key | Context | Action |
-|---|---|---|
-| Any printable key | Canvas focus | Open spotlight pre-seeded |
-| `/` | Canvas focus | Open spotlight empty |
-| `Esc` | Spotlight open | Close / finish + place krate |
-| `↑/↓` | Spotlight | Navigate results |
-| `Tab` | Spotlight | Apply type filter / cycle filters |
-| `⌥L/S/D/Y` | Spotlight (result selected) | Open that view |
-| `Enter` | Spotlight | Open default view + close |
-| `↑/↓` | Collection filter focused | Move row selection |
-| `⌥L/S/D/Y` | Collection filter focused | Open that view for selected row |
-| `Enter` | Collection filter focused | Open default view for selected row |
-| `Space` + drag | Canvas | Pan (even over windows/shells) |
-| `Ctrl/⌘` + scroll | Anywhere | Zoom canvas |
-| `z` | Mouse over window | Maximize / restore window |
-
----
-
-## File Reference
-
-| File | Purpose |
-|---|---|
-| `Krates.dc.html` | Full interactive prototype — the primary design reference. Open in any modern browser. |
-| `README.md` | This document |
-
----
-
-## Implementation Notes & Gotchas
-
-1. **Space pan over shells:** The trickiest keyboard interaction. The space key must pan the canvas even when a `