# Admin Drawer Feature Specification ## Overview The admin drawer is a right-side panel that displays all users connected to the current yard (shared cluster workspace). It enables administrators to see real-time user activity and spectate on other users' workspaces. ## Trigger ### Activation - Button: `◉ admin` in top bar - Toggle: Click to open/close - Close on: clicking backdrop, pressing Esc ### Modal Overlay - Backdrop: `rgba(7,9,13,.4)` - Click backdrop: Close drawer - Click outside: Close drawer ## Drawer Layout ### Container - Width: 380px - Position: `absolute; right: 0; top: 0; bottom: 0; z-index: 56` - Background: `rgba(13,17,24,.98)` - Border: `border-left: 1px solid rgba(140,165,200,.2)` - Animation: Slide-in from right ### Content Structure - Header (fixed, sticky) - Scrollable user list - Footer (optional, for actions) ## User Card Structure For each connected user (including self): ### Avatar Section - Circle: 40px直径 - Background: User color from palette - Content: - For self: `★` (star) - For others: 2-letter initials - Hover: Show full name + status tooltip ### Identity - Name (bold) - Status: - Active: `active · ns/payments` - Idle: `idle 5m` (or `idle Nm`) ### Activity Info - Current spotlight query (if open) - Display: `🔍 pods in payments` - "typing…" pulsing indicator (if currently typing) - Krate count: `N krates open` ### Open Krate List Collapsible section showing each active krate: - Clickable row - Content: - Krate name - View-letter badges: `Y`, `D`, `L`, `S` - Status dot (health indicator) - **Spectate**: Click any krate row to: - Close admin panel - Fly camera to that krate - Zoom to center the krate at `zoom: 0.92` ### Self User Special Case - Real-time state updates - Shows actual spotlight query - Lists user's actual open krates - Updates as user interacts ## Real-Time Updates ### Presence Protocol Each client publishes to awareness channel: ``` { userId: string, name: string, color: string, lastActive: timestamp, query: string | null, // spotlight query krateIds: string[], // list of open krate IDs cursor: { x, y } | null // optional cursor position } ``` ### Update Frequency - On connection/disconnection: immediate - On spotlight open/close: immediate - On krate create/delete: immediate - Periodic heartbeat: every 5 seconds ## Keyboard Navigation | Key | Context | Action | |---|---|---| | Esc | Admin drawer open | Close drawer | | ↑ / ↓ | Drawer focused | Navigate user cards | | Click user card | User list | Expand/collapse krate list | | Click krate row | Krate list | Spectate (fly to krate) | | Click backdrop | Anywhere | Close drawer | ## User Sorting - Self user: Always first - Active users: Ordered by last active (most recent first) - Idle users: Ordered by last active - Collapsed by default: Minimize height ## Visual Feedback ### Active/Idle Indicators - Active: Solid color avatar, no dimming - Idle: Dimmed avatar (80% opacity), `idle Nm` label ### Typing Indicator - Text: `typing…` - Color: Muted accent - Animation: Pulsing opacity or small bounce - Show when user has spotlight open AND hasn't typed in <1s ### Status Dots - Krate status: Green/amber/red dot (matching window content) - Pod status dot: Already in krate view (reuse same color logic) ## Admin Features (Optional Future) ### Control Actions - Notify user: Send notification to specific user - Suspend: Temporarily disconnect user (for debugging) - View logs: See admin-level logs per user ### Spectate Controls - Step forward/backward: Next/previous krate - Cycle through users: Left/right arrow - Lock view: Prevent camera from moving (spectator mode) ## State Management ### Drawer State ```typescript interface AdminState { open: boolean; users: { id: string; name: string; color: string; self: boolean; status: 'active' | 'idle'; idleMinutes?: number; query?: string; typing: boolean; krateIds: string[]; }[]; focusedUserId: string | null; } ``` ### User State ```typescript interface UserPresence { userId: string; name: string; color: string; lastActive: number; // timestamp lastTyping: number; // timestamp spotlightQuery: string | null; openKrates: KratePresence[]; } interface KratePresence { krateId: string; label: string; viewCount: number; views: string[]; // ['Y', 'D', 'L', 'S'] status: string; // from first window } ``` ## Gotchas 1. **Self-updates**: User's own card must show real-time state, not cached data 2. **Focus management**: Keep focus on drawer when open, trap Tab cycle 3. **Idle detection**: 60 seconds of no activity = idle 4. **Typing indicator**: Debounce by 1000ms after last keypress 5. **Spectate behavior**: Close admin panel first, THEN fly camera 6. **Roster overflow**: Handle >10 users gracefully (scroll, paginate, or truncate)