Add design folder and README
This commit is contained in:
567
README.md
Normal file
567
README.md
Normal file
@@ -0,0 +1,567 @@
|
|||||||
|
# Handoff: Krates — Kubernetes Visual Cluster Navigator
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
The product lives at **krates.io**.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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:** `<pre>` 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 <pod> -n <ns> -- 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 `<textarea>` or xterm.js instance has focus. Implement with a `document.addEventListener('keydown', handler, { capture: true })` that checks if space is pressed during a drag gesture, sets a flag, and calls `e.preventDefault()` on the focused element. Do NOT use `e.stopPropagation()` — you still need the event to reach other handlers.
|
||||||
|
|
||||||
|
2. **Ctrl/⌘+scroll over windows:** Similarly, `wheel` events on window content areas will be captured by the scroll container. Use `{ capture: true }` on the canvas wheel handler to intercept before the window, check for `e.ctrlKey || e.metaKey`, and only zoom then. Otherwise let the event scroll the window content.
|
||||||
|
|
||||||
|
3. **LOD hysteresis:** Use separate thresholds for collapse (0.4) and expand (0.5) to prevent jitter. Store `collapsed` as boolean in state, not derived from `zoom` on every render.
|
||||||
|
|
||||||
|
4. **Non-overlapping krate placement:** On each new krate creation, try the computed default position, then nudge by fixed increments until no bounding-box overlap exists with any existing krate. On drag release, do the same nudge.
|
||||||
|
|
||||||
|
5. **Fuzzy search speed:** The prototype scores all objects on every keystroke. For a real cluster with thousands of objects, debounce by 16ms (one animation frame) or run scoring in a Web Worker. The scoring algorithm is simple enough that even 10,000 objects should score in <5ms.
|
||||||
|
|
||||||
|
6. **Collection filter auto-focus:** When a collection krate is created, `focus()` its filter `<input>` in the next tick. Use a ref stored during render and a flag cleared in `componentDidUpdate` / `useEffect`.
|
||||||
|
|
||||||
|
7. **Secret decoding:** The prototype shows decoded secrets inline with a banner. In the real app, ensure that decoded secrets are handled carefully — consider requiring explicit user action to reveal, and never log them.
|
||||||
|
|
||||||
|
8. **CRDT sync scope:** Sync krate positions, existence, and window layout via CRDT. Do NOT sync window content (logs, YAML, shell) — each client streams independently from the k8s API. Only sync the *structure* of the workspace.
|
||||||
|
|
||||||
|
9. **Resize handles:** The prototype does not implement drag-resize yet. When implementing, a resize in one column/row should proportionally adjust sibling windows. Store a `colWidths` and `rowHeights` array per krate, not absolute `dx/dy` per window.
|
||||||
|
|
||||||
|
10. **Admin presence data:** The prototype uses hardcoded mock users. In the real app, use Yjs awareness or a presence channel: each client publishes `{ userId, name, color, query, krateIds[] }` as ephemeral state; the admin panel subscribes and renders it reactively.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Recommended Tech Stack
|
||||||
|
|
||||||
|
```
|
||||||
|
Frontend: React 18 + TypeScript
|
||||||
|
Styling: CSS-in-JS (styled-components or vanilla-extract) or Tailwind
|
||||||
|
Terminal: xterm.js
|
||||||
|
Real-time: Yjs + y-websocket (CRDT collaboration)
|
||||||
|
Backend: Go + kubernetes/client-go + gorilla/websocket
|
||||||
|
Fonts: IBM Plex Sans + IBM Plex Mono (Google Fonts or self-hosted)
|
||||||
|
Build: Vite
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Backlog (Not in Prototype)
|
||||||
|
|
||||||
|
- **Tile layout presets** per krate: "logs hero" (one large log window, data tiles around it), "split", "grid". Shortcut on krate header.
|
||||||
|
- **Port-forwarding window**: dedicated view type alongside YAML/Logs/Shell/Describe
|
||||||
|
- **Events window**: `kubectl get events --watch` for a namespace or object
|
||||||
|
- **Diff view**: compare YAML between two versions or two objects
|
||||||
|
- **Multi-cluster support**: multiple yards in one app, switchable from the top bar
|
||||||
26
design/canvas.md
Normal file
26
design/canvas.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Canvas Feature
|
||||||
|
|
||||||
|
**Purpose**: Infinite zoomable/pannable workspace where krates live. Starts empty.
|
||||||
|
|
||||||
|
**Layout**:
|
||||||
|
- Full‑viewport `position: fixed; inset: 0` with background `#0b0e13`.
|
||||||
|
- Two CSS grid overlays (fine `34px` rgba(125,145,175,.04), coarse `170px` rgba(125,145,175,.075)).
|
||||||
|
- World layer: `<div>` sized `12000×8000px`, transformed via `translate(camX, camY) scale(zoom)`; all krates/windows are absolutely positioned children.
|
||||||
|
- Top bar (`height ~56px`, `z-index:10`).
|
||||||
|
- Bottom hint bar (`right:18px; bottom:18px`).
|
||||||
|
- Zoom pill (`left:18px; bottom:18px`).
|
||||||
|
- Minimap (`right:18px; bottom:64px; width:180px; height:120px`).
|
||||||
|
|
||||||
|
**Navigation**:
|
||||||
|
- Scroll wheel → pan.
|
||||||
|
- Ctrl/⌘ + scroll → zoom.
|
||||||
|
- Space + drag → pan (global capture, works even when a shell has focus).
|
||||||
|
- Click empty canvas → open Spotlight.
|
||||||
|
- Typing any key (when no input focused) → open Spotlight pre‑seeded with that character.
|
||||||
|
|
||||||
|
**LOD**:
|
||||||
|
- `zoom > 0.5` normal view (krates expanded, windows visible).
|
||||||
|
- `zoom < 0.4` collapsed view (krates become 230 px overview cards, windows hidden).
|
||||||
|
- Hysteresis: collapse @0.4, expand @0.5.
|
||||||
|
|
||||||
|
**Camera fly**: on programmatic open, animate to center krate with `zoom:0.92` using `transform .52s cubic‑bezier(.22,.8,.28,1)`.
|
||||||
45
design/collection_window.md
Normal file
45
design/collection_window.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# Collection Window Feature
|
||||||
|
|
||||||
|
**Purpose**: Provide an overview list or tree of objects within a namespace or category (e.g., "All Pods", "ns/payments").
|
||||||
|
|
||||||
|
**Size**: Default 540 × 480 px.
|
||||||
|
|
||||||
|
**Header**:
|
||||||
|
- Title (e.g., "pods · ns/payments") with status summary badges (✓ N / ⚠ N / ✗ N).
|
||||||
|
- Filter input (auto‑focused on open) using `IBM Plex Mono` at 12 px.
|
||||||
|
- Keyboard hint (`↑↓ ⌥L/S/D/Y`).
|
||||||
|
- Buttons to toggle **List** vs **Tree** view.
|
||||||
|
|
||||||
|
**List Mode**:
|
||||||
|
- Row shows shape glyph, name, relation tag (muted), health metric, type badge, view shortcuts (⌥L/S/D/Y), status dot.
|
||||||
|
- Selected row: `background: accentDim; border-left: 2px solid accent`.
|
||||||
|
- Hover row: `background: rgba(255,255,255,.04)`.
|
||||||
|
- Sorting: degraded/failed first, then pending, then ready; alphabetical within groups.
|
||||||
|
|
||||||
|
**Tree Mode**:
|
||||||
|
- Hierarchical view similar to k9s "xray".
|
||||||
|
- Workloads expand to show their pods; pods expand to show ConfigMaps, Secrets, PVCs.
|
||||||
|
- Services/Ingresses expand to show targets.
|
||||||
|
- Relation tags: `pod`, `configMap`, `secret`, `volume`, `selects`, `routes`, `used by`.
|
||||||
|
- Indentation `20 px × depth` with L‑shaped connector border.
|
||||||
|
- Filtering retains matching nodes **and** all ancestors so hierarchy stays readable.
|
||||||
|
|
||||||
|
**Interaction**:
|
||||||
|
- Typing filters instantly (no mode switch).
|
||||||
|
- `↑/↓` moves selection highlight.
|
||||||
|
- `⌥L/S/D/Y` opens the respective view for the selected row (same shortcuts as Spotlight).
|
||||||
|
- `Enter` opens the default view for the selected row.
|
||||||
|
- `Escape` blurs the filter input.
|
||||||
|
- Mouse‑over the collection window and start typing focuses the filter input.
|
||||||
|
- Resizing follows the same rules as other Detail Windows (sibling windows resize proportionally).
|
||||||
|
|
||||||
|
**State** (`CollectionState` interface in design docs):
|
||||||
|
```typescript
|
||||||
|
interface CollectionState {
|
||||||
|
[wid: string]: {
|
||||||
|
search: string;
|
||||||
|
view: 'list' | 'tree';
|
||||||
|
sel: number; // selected row index
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
26
design/detail_window.md
Normal file
26
design/detail_window.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
# Detail Window Feature
|
||||||
|
|
||||||
|
**Purpose**: Shows a single view (YAML, Describe, Logs, Shell) for a Kubernetes object.
|
||||||
|
|
||||||
|
**Size**: Default 380×362 px, resizable via bottom‑right corner handle. Sibling windows in the same column/row resize proportionally.
|
||||||
|
|
||||||
|
**Header (36 px)**:
|
||||||
|
- Shape glyph (type‑specific colour) + view label (uppercase, accent colour).
|
||||||
|
- Object name + status badge.
|
||||||
|
- Close button (×).
|
||||||
|
- `cursor: grab` – drag to reposition within the canvas.
|
||||||
|
- Double‑click → maximize to fill available space (below top bar, above bottom bar, side margins 22 px). Press `z` or double‑click again to restore.
|
||||||
|
|
||||||
|
**Tab Bar**: YAML · Describe · Logs · Shell. Shell tab disabled for non‑exec objects (opacity 0.45).
|
||||||
|
|
||||||
|
**Content area**: `<pre>` with `overflow:auto; height:286px; font-family:'IBM Plex Mono'; font-size:12px; line-height:1.65`. Custom scrollbar (8 px, rgba(140,165,200,.18)).
|
||||||
|
|
||||||
|
**Keyboard focus**:
|
||||||
|
- Mouse‑over window + type → focuses that window’s content (shell input when a Shell view is active).
|
||||||
|
- Global `keydown` capture routes keys to the hovered window if it is a shell.
|
||||||
|
|
||||||
|
**Maximize (`z` key or double‑click header)**:
|
||||||
|
- Resize to `width = viewport.width - 44px`, `height = viewport.height - topBarHeight(68px) - bottomBarHeight(66px)`.
|
||||||
|
- Set camera so the window fills the viewport (`zoom = 1`).
|
||||||
|
|
||||||
|
**Secret YAML auto‑decode**: When rendering a Secret, base64‑decode all `data` values and display plaintext with a banner `⊙ secret values auto‑decoded`.
|
||||||
39
design/krate.md
Normal file
39
design/krate.md
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Krate Feature
|
||||||
|
|
||||||
|
**Purpose**: Named group of windows for a single Kubernetes object (or a collection) placed on the canvas.
|
||||||
|
|
||||||
|
**Visual Container**:
|
||||||
|
- Dashed border frame 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 plus 30 px padding.
|
||||||
|
- Header bar above the frame shows object name, status badge, window count, a drag handle, and a close (×) button.
|
||||||
|
|
||||||
|
**Layout of Windows**:
|
||||||
|
- Tiled in a 2‑column grid.
|
||||||
|
- Column 0 offset `dx:0`, column 1 offset `dx:412`.
|
||||||
|
- Row height `416 px`.
|
||||||
|
- When a window is resized, sibling windows resize proportionally within the same column/row.
|
||||||
|
- Windows never overlap – on drag release the krate checks other krate bounding boxes and nudges to avoid overlap (gentle snap).
|
||||||
|
|
||||||
|
**Interactions**:
|
||||||
|
- **Drag header** → move entire krate.
|
||||||
|
- **Double‑click header** → toggle collapse/expand.
|
||||||
|
- **Minimize button (—)** → collapse to overview card (230 px wide) showing name, view‑letter badges, status dot, window count, namespace.
|
||||||
|
- **Double‑click collapsed card** → re‑expand and fly camera to the krate.
|
||||||
|
- **Non‑overlapping placement**: on mouse release after a drag, the krate adjusts position to avoid overlapping other krates.
|
||||||
|
- **Camera fly‑to** after summon: animates to centre the krate at `zoom: 0.92`.
|
||||||
|
|
||||||
|
**State** (`Krate` interface in design docs):
|
||||||
|
```typescript
|
||||||
|
interface Krate {
|
||||||
|
id: string;
|
||||||
|
objId: string | null; // null for collection krates
|
||||||
|
collScope?: { kind: 'namespace' | 'category', value: string };
|
||||||
|
label: string;
|
||||||
|
status: string;
|
||||||
|
color: string; // derived from namespace palette
|
||||||
|
wx: number; wy: number; // world position
|
||||||
|
minimized: boolean;
|
||||||
|
windows: Window[];
|
||||||
|
seq: number; // for generating window IDs
|
||||||
|
}
|
||||||
|
```
|
||||||
17
design/logs_window.md
Normal file
17
design/logs_window.md
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
# Logs Window Feature
|
||||||
|
|
||||||
|
**Purpose**: Live‑tail container logs for a pod/deployment/statefulset.
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
- Backend streams `kubectl logs --follow` over a WebSocket.
|
||||||
|
- Front‑end displays lines in the same `<pre>` area as Detail Window.
|
||||||
|
- Auto‑scroll to bottom when new lines arrive unless the user has manually scrolled up (detect `scrollTop + clientHeight < scrollHeight`).
|
||||||
|
- Color‑code log levels: `ERROR` → `#ef6f6f`, `WARN` → `#e8b54a`, `INFO` → `#b9c6d8`.
|
||||||
|
- No input handling – all keypresses are routed to the canvas (unless the user focuses the filter in a collection window).
|
||||||
|
|
||||||
|
**Interaction**:
|
||||||
|
- Click on a result in Spotlight or Collection with **Alt+L** to open a Logs window inside a new or existing Krate.
|
||||||
|
- Resize like any Detail Window; sibling windows resize proportionally.
|
||||||
|
- Double‑click header or press `z` to maximize.
|
||||||
|
|
||||||
|
**State** (`Window` type `logs`).
|
||||||
18
design/shell_window.md
Normal file
18
design/shell_window.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# Shell Window Feature
|
||||||
|
|
||||||
|
**Purpose**: Interactive pseudo‑terminal session inside a krate.
|
||||||
|
|
||||||
|
**Implementation**:
|
||||||
|
- Front‑end component uses **xterm.js** (or equivalent) for terminal UI.
|
||||||
|
- Backend opens a WebSocket that proxies to `kubectl exec -it <pod> -n <ns> -- sh` (or the container's default shell).
|
||||||
|
- Input handling: when the mouse is over the shell window, all keypresses are forwarded to the terminal; space does not trigger Spotlight.
|
||||||
|
- Scroll wheel over the terminal scrolls its content; `Ctrl/⌘+scroll` still zooms the canvas.
|
||||||
|
- No PTY needed on the server side beyond the exec stream; the WebSocket carries stdin/stdout/stderr.
|
||||||
|
|
||||||
|
**Interaction**:
|
||||||
|
- Click a result in Spotlight or Collection and press **Alt+S** to open a Shell window in a new/existing Krate.
|
||||||
|
- Resize like any Detail Window; sibling windows resize proportionally.
|
||||||
|
- Double‑click header or press `z` to maximize to the viewport.
|
||||||
|
- The shell window obeys the global pan/zoom shortcuts (Space‑drag pans, Ctrl‑scroll zooms) when the cursor is inside it.
|
||||||
|
|
||||||
|
**State** (`Window` type `shell`).
|
||||||
49
design/spotlight.md
Normal file
49
design/spotlight.md
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
# Spotlight Feature
|
||||||
|
|
||||||
|
**Purpose**: Global search overlay that lets users find any Kubernetes object, existing krates, namespaces, or collection categories, and open specific views.
|
||||||
|
|
||||||
|
**Trigger**:
|
||||||
|
- Click on empty canvas.
|
||||||
|
- Type any printable key when no input is focused (pre‑seeds query).
|
||||||
|
- `/` key opens an empty spotlight.
|
||||||
|
|
||||||
|
**Layout**:
|
||||||
|
- Full‑screen dark backdrop `rgba(7,9,13,.55)` with `backdrop‑filter: blur(2px)`.
|
||||||
|
- Centered panel (max 660 px, 93 % viewport width) with background `rgba(16,20,28,.97)`, border `1px solid rgba(140,165,200,.26)`, border‑radius `14px`.
|
||||||
|
- Input row (≈ 52 px high) containing:
|
||||||
|
- Search glyph `⌕` (accent color).
|
||||||
|
- Optional type‑filter pill.
|
||||||
|
- Text input (font‑size 18 px, IBM Plex Sans).
|
||||||
|
- Optional Tab‑ghost hint.
|
||||||
|
- Horizontal **type chips** row (scrollable on narrow screens) for all object kinds (`pods`, `svc`, `deploy`, `secret`, `cm`, `sts`, …).
|
||||||
|
- Results list (max‑height 48 vh, overflow‑auto) – each row shows:
|
||||||
|
- Shape glyph (type‑specific).
|
||||||
|
- Name.
|
||||||
|
- Subtext `type · ns/namespace`.
|
||||||
|
- CRD badge if applicable.
|
||||||
|
- Type badge.
|
||||||
|
- **View chips** appear on the selected result only: `⌥L logs · ⌥S shell · ⌥D describe · ⌥Y yaml`. Views are limited to object capabilities (e.g., logs & shell only for pods, deployments, daemonsets, statefulsets).
|
||||||
|
- Footer hint line.
|
||||||
|
|
||||||
|
**Fuzzy Search Algorithm** (used in the prototype):
|
||||||
|
- Score = character‑match score × 10 + type boost.
|
||||||
|
- Type boost values: crd 60, deployment 50, statefulset 48, service 46, daemonset 44, ingress 42, secret 24, configmap 22, pvc 18, pod 16.
|
||||||
|
- Character match: sequential chars, +3 for consecutive, +5 for word‑boundary start.
|
||||||
|
- Top 8 results displayed.
|
||||||
|
|
||||||
|
**Tab Quick‑Filter**:
|
||||||
|
- Typing a type alias (`pod`, `svc`, `deploy`, etc.) shows a ghost hint `⇥ Pods`.
|
||||||
|
- Press **Tab** to lock filter as a pill; Backspace removes it.
|
||||||
|
- Tab also cycles through recently used type filters (most‑recent first).
|
||||||
|
|
||||||
|
**Keyboard Shortcuts** (Alt/Option required):
|
||||||
|
- `Alt+L` = open **Logs** view for selected result.
|
||||||
|
- `Alt+S` = open **Shell** view.
|
||||||
|
- `Alt+D` = open **Describe** view.
|
||||||
|
- `Alt+Y` = open **YAML** view.
|
||||||
|
- `Enter` = open default view (logs for pods, describe for workloads, yaml for config resources) and close spotlight.
|
||||||
|
- `Esc` = close spotlight; if a krate was summoned it is placed on canvas.
|
||||||
|
- `↑/↓` = navigate results.
|
||||||
|
- `Tab` = apply/cycle type filter.
|
||||||
|
|
||||||
|
**Auto‑Close**: after 500 ms of keyboard idle the spotlight auto‑closes and the camera flies to the newly created krate.
|
||||||
19
design/top_bar.md
Normal file
19
design/top_bar.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# Top Bar Feature
|
||||||
|
|
||||||
|
**Purpose**: Persistent UI overlay providing cluster status, krate count, sync indicator, admin controls, and user roster.
|
||||||
|
|
||||||
|
**Layout**:
|
||||||
|
- `display: flex; align-items: center; padding: 13px 18px;` transparent over canvas.
|
||||||
|
- **Left side**:
|
||||||
|
- **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)`. Diamond glyph with `clip-path: polygon(50% 0,100% 50%,50% 100%,0 50%)` in accent color.
|
||||||
|
- **Cluster pill**: shows cluster name, green health dot (`#4ad07a`) with `box-shadow: 0 0 8px #4ad07a`.
|
||||||
|
- **Krate count pill**: visible when krates exist.
|
||||||
|
- **Right side**:
|
||||||
|
- **Synced pill**: pulsing accent dot + text "synced".
|
||||||
|
- **Admin button**: `◉ admin` – outlined when active, muted otherwise.
|
||||||
|
- **Roster avatars**: overlapping circles (`margin-left: -7px`), 30×30 px, colored per user, initials (★ for self).
|
||||||
|
|
||||||
|
**Behavior**:
|
||||||
|
- Stays fixed at top; does not scroll with canvas.
|
||||||
|
- Clicking **Admin button** toggles the Admin Drawer.
|
||||||
|
- Avatars show presence via Yjs awareness; hovering shows tooltip with username.
|
||||||
Reference in New Issue
Block a user