# Mission Control Dashboard — Bug & Improvement Review > Systematic code review of `__LOCAL_LLMs/dashboard/` (6 source files, 1,395 lines) > Last updated: Feb 19, 2026 --- ## File Inventory | File | Lines | Purpose | | ------------------------------------ | ----- | -------------------------------------------------------------------- | | `src/app/page.tsx` | 1,079 | Main dashboard UI (single component) | | `src/app/globals.css` | 91 | Design tokens, animations, base styles | | `src/app/layout.tsx` | 20 | Root layout (metadata, dark mode) | | `src/app/api/ollama/route.ts` | 117 | Ollama REST proxy (list, load, unload, pull, delete, show, generate) | | `src/app/api/ollama/stream/route.ts` | 38 | Ollama streaming generate proxy (NDJSON) | | `src/app/api/whisper/route.ts` | 66 | Whisper binary + GGML model discovery | | `src/app/api/system/route.ts` | 162 | System info (chip, memory via vm_stat, disk, brew) | **Stack:** Next.js 16, React 19, TailwindCSS v4, Lucide icons, TypeScript --- ## 1. Bugs - [ ] **B1. Hardcoded machine specs in header** — `page.tsx:317` Subtitle reads `Apple M4 Pro · 48 GB · {system?.platform}` — should use `system?.chip` and `formatBytes(system?.memory.total)` dynamically so it works on any machine. - [ ] **B2. Pull model blocks UI — no progress feedback** — `api/ollama/route.ts:84-92` `handlePull` calls Ollama with `stream: false`. Large models (20+ GB) block for 30+ minutes. The Next.js API route will likely timeout. Must use `stream: true` and pipe progress events to the client. _(Combined with F1.)_ - [ ] **B3. Dead code: non-streaming `generate` action** — `api/ollama/route.ts:69-82` The `action === 'generate'` handler is unused — UI only uses `/api/ollama/stream`. Remove or keep as fallback with a comment. - [ ] **B4. Escape key closes modal during active streaming** — `page.tsx:188-197` Global `keydown` handler calls `setPromptModel(null)` unconditionally. Backdrop click correctly checks `!promptLoading`. Escape should also respect `promptLoading` to prevent discarding an in-flight response. - [ ] **B5. Auto-refresh (15s) fires during streaming/pull** — `page.tsx:182-185` `setInterval(fetchAll, 15000)` runs unconditionally. During streaming this causes background churn and potential UI flicker. Should pause while `promptLoading` or `pullLoading` is true. - [ ] **B6. Toast ID collision on HMR remount** — `page.tsx:156-159` `toastId.current` resets to 0 on component remount during dev. Use `Date.now()` or `crypto.randomUUID()` for robust uniqueness. - [ ] **B7. vm_stat page size hardcoded** — `api/system/route.ts:103` Hardcoded `16384`. Should parse from vm_stat's first line: `"(page size of NNNNN bytes)"` for portability. - [ ] **B8. Whisper models dir not configurable** — `api/whisper/route.ts:24` Hardcoded to `~/whisper-models`. Should scan multiple known paths (`/opt/homebrew/share/whisper-cpp/models/`, `~/whisper-models`, `~/.cache/whisper/`) or accept `WHISPER_MODELS_DIR` env var. - [ ] **B9. No AbortController for streaming fetch** — `page.tsx:250-289` Closing the prompt modal doesn't cancel the underlying fetch. The `reader.read()` loop continues in the background wasting CPU/bandwidth until the model finishes generating. - [ ] **B10. Brew shows "Loading..." when array is empty** — `page.tsx:936-940` When `system.brewPackages` is `[]` (all uninstalled), displays "Loading..." instead of "No packages found". Needs to distinguish "still fetching" vs "fetched but empty". - [ ] **B11. Prompt text not cleared on close without send** — `page.tsx:951-957` Backdrop click clears `promptText`, but Escape handler (B4 fix) should also clear it. Otherwise stale text persists when re-opening. --- ## 2. Code Quality - [ ] **CQ1. Monolithic 1,079-line single component** — `page.tsx` All interfaces, utilities, sub-components, and 900+ lines of JSX in one file. Extract to: - `components/` — StatusDot, ProgressBar, ToastContainer, PromptModal, OllamaModelsPanel, SystemPanel, WhisperPanel, BrewPanel - `lib/types.ts` — interfaces (OllamaModel, SystemData, etc.) - `lib/format.ts` — formatBytes, formatUptime - `lib/hooks.ts` — useAutoRefresh, useToasts, useOllamaActions - [ ] **CQ2. Pervasive inline styles instead of CSS/Tailwind classes** — `page.tsx` (100+ occurrences) Every `style={{ color: 'var(--text-tertiary)' }}` should be a utility class. Options: custom Tailwind theme mapping, or CSS utility classes in `globals.css` (e.g., `.text-muted`). - [ ] **CQ3. OLLAMA_URL duplicated** — `api/ollama/route.ts:3` + `api/ollama/stream/route.ts:3` Same `process.env.OLLAMA_URL || 'http://localhost:11434'` in two files. Extract to `lib/ollama-config.ts`. - [ ] **CQ4. No React Error Boundary** — `page.tsx` Unexpected API response shape crashes the entire dashboard. Add an `error.tsx` (Next.js App Router convention) for graceful recovery. - [ ] **CQ5. No loading skeleton / shimmer UI** Initial load shows "..." placeholders. Skeleton cards would be more polished. - [ ] **CQ6. No TypeScript strict null checks in API responses** API route handlers catch errors but return loosely typed JSON. Add Zod validation on the Ollama/system responses to prevent runtime surprises. --- ## 3. Features - [ ] **F1. Streaming pull with progress bar** _(fixes B2)_ Use Ollama `stream: true` for `/api/pull`. Create `/api/ollama/pull/route.ts` that pipes NDJSON progress. UI shows progress bar with `completed/total` bytes, speed, and ETA. - [ ] **F2. Model search/filter** Search input above models list. Filter by name, family, quantization. Useful when 10+ models are installed. - [ ] **F3. Prompt history (localStorage)** Store last 20 prompts with model name + timestamp. Dropdown in prompt modal to re-run previous prompts. - [ ] **F4. Chat mode (multi-turn conversation)** Use Ollama `/api/chat` instead of `/api/generate`. Chat bubble layout with message history. System prompt input field. - [ ] **F5. Model comparison (side-by-side)** Send same prompt to 2 models simultaneously. Display responses side-by-side with latency/quality comparison. - [ ] **F6. Token/s metrics after generation** Parse `eval_count` and `eval_duration` from the final NDJSON chunk. Display tokens/second, total tokens, and latency in the response footer. - [ ] **F7. System resource sparklines (time-series)** Ring buffer of memory/CPU snapshots (localStorage). Render mini sparkline charts in the System panel. Spot trends over time. - [ ] **F8. Ollama server logs viewer** Read `~/.ollama/logs/` and display in a collapsible terminal-style panel. Filter by level. Auto-scroll. - [ ] **F9. Modelfile / template viewer** The `show` action already fetches Modelfile, template, and system prompt. Display in a collapsible code block in expanded model details. - [ ] **F10. Dark/light theme toggle** Add `:root.light` CSS variable overrides. Theme toggle with localStorage persistence. Current architecture supports this natively. - [ ] **F11. Keyboard shortcuts panel (`?` key)** Show all shortcuts in a modal: ⌘+Enter (send), Esc (close), R (refresh), / (search models), ? (help). - [ ] **F12. Whisper transcription test** Upload/record a short audio clip, transcribe locally via whisper-cli, display result with latency. Tests the full local STT pipeline. - [ ] **F13. Responsive mobile layout** Better breakpoints for the 4-column stats row and 3-column main grid. Collapsible sidebar on mobile. - [ ] **F14. Model tags/labels (localStorage)** User-defined tags (coding, fast, vision) with colored badges. Persisted in localStorage. - [ ] **F15. Extraction service integration panel** Show extraction-service (port 4005) health status. Run test extractions against loaded Ollama models. Bridges dashboard to LysnrAI pipeline. - [ ] **F16. Auto-load preferred model** Mark a model as "auto-load" (stored in localStorage). When Ollama is online but no models loaded, auto-load the preferred model. --- ## 4. Performance & Reliability - [ ] **P1. No request deduplication on Refresh** — `page.tsx:164-176` Rapid clicks on Refresh fire duplicate `fetchAll()` calls. Add a `fetchingRef` guard or disable the button during fetch (partially done for `actionLoading` but not for `fetchAll`). - [ ] **P2. Static cache never expires** — `api/system/route.ts:81-90` `staticCache` (chip, GPU, brew) lives forever in the server process. Brew package upgrades won't reflect. Add 5-minute TTL. - [ ] **P3. `du -sk ~/.ollama/models` on every refresh** — `api/system/route.ts:41` Traverses entire models directory every 15 seconds. Cache with 60-second TTL. - [ ] **P4. No fetch timeout on Ollama calls** — `api/ollama/route.ts:5-12` `fetchOllama` has no `AbortSignal` or timeout. If Ollama hangs, the dashboard hangs. Add 5-second timeout. - [ ] **P5. `system_profiler` slow on first load** — `api/system/route.ts:52-53` Takes ~2-3 seconds. Cached after first call, but first dashboard load waits. Consider eager background fetch on server start or return placeholder. --- ## 5. Security & Hardening - [ ] **S1. No input validation on model names** — `api/ollama/route.ts:50-51` `model` from request body passed directly to Ollama. Add regex validation: `^[a-zA-Z0-9._:/-]{1,256}$`. - [ ] **S2. Shell command interpolation pattern** — `api/system/route.ts:67` `execAsync(\`brew list --versions ${pkg}\`)`— safe today (hardcoded targets) but fragile. Use`execFile('brew', ['list', '--versions', pkg])` for safety. - [ ] **S3. No CORS or auth** _(acceptable for local-only)_ Any local process can call API routes. Fine for dev tool; document the assumption. --- ## 6. Implementation Tracker ### Sprint 1 — Critical Bug Fixes _(est. 1–2 hrs)_ | # | ID | Task | Effort | Commit | | --- | --------- | ----------------------------------------- | ------ | ------ | | 1 | - [ ] B4 | Guard Escape key during streaming | 5 min | | | 2 | - [ ] B5 | Pause auto-refresh during prompt/pull | 10 min | | | 3 | - [ ] B9 | Add AbortController to streaming fetch | 15 min | | | 4 | - [ ] B1 | Dynamic chip/RAM in header | 5 min | | | 5 | - [ ] B11 | Clear prompt text on Escape close | 5 min | | | 6 | - [ ] P4 | Add timeout to Ollama fetch calls | 10 min | | | 7 | - [ ] B3 | Remove dead generate action (or document) | 5 min | | | 8 | - [ ] B6 | Use Date.now() for toast IDs | 2 min | | | 9 | - [ ] B10 | Fix brew "Loading..." vs "empty" state | 5 min | | ### Sprint 2 — Pull Progress + Metrics _(est. 2–3 hrs)_ | # | ID | Task | Effort | Commit | | --- | ----------- | ----------------------------------- | ------ | ------ | | 10 | - [ ] B2+F1 | Streaming pull with progress bar | 60 min | | | 11 | - [ ] F6 | Display tokens/s after generation | 30 min | | | 12 | - [ ] B7 | Parse vm_stat page size dynamically | 10 min | | | 13 | - [ ] B8 | Multi-path whisper model discovery | 15 min | | ### Sprint 3 — Component Refactor _(est. 2–3 hrs)_ | # | ID | Task | Effort | Commit | | --- | --------- | --------------------------------------- | ------ | ------ | | 14 | - [ ] CQ1 | Extract components into separate files | 90 min | | | 15 | - [ ] CQ4 | Add error.tsx Error Boundary | 15 min | | | 16 | - [ ] CQ3 | Shared ollama-config.ts | 10 min | | | 17 | - [ ] CQ2 | Consolidate inline styles → CSS classes | 45 min | | | 18 | - [ ] S1 | Add model name input validation | 10 min | | | 19 | - [ ] S2 | Replace exec → execFile for brew | 10 min | | ### Sprint 4 — UX Enhancements _(est. 3–4 hrs)_ | # | ID | Task | Effort | Commit | | --- | ----------- | ------------------------------------ | ------ | ------ | | 20 | - [ ] F3 | Prompt history (localStorage) | 45 min | | | 21 | - [ ] F6+F9 | Token metrics + Modelfile viewer | 30 min | | | 22 | - [ ] F4 | Chat mode (multi-turn via /api/chat) | 90 min | | | 23 | - [ ] F2 | Model search/filter | 30 min | | | 24 | - [ ] F11 | Keyboard shortcuts panel | 20 min | | ### Sprint 5 — Integration & Polish _(est. 2–3 hrs)_ | # | ID | Task | Effort | Commit | | --- | ----------- | -------------------------- | ------ | ------ | | 25 | - [ ] F15 | Extraction service panel | 60 min | | | 26 | - [ ] F12 | Whisper transcription test | 45 min | | | 27 | - [ ] F7 | System resource sparklines | 45 min | | | 28 | - [ ] CQ5 | Loading skeleton UI | 20 min | | | 29 | - [ ] P1-P3 | Request dedup + cache TTLs | 30 min | | | 30 | - [ ] F16 | Auto-load preferred model | 20 min | | ### Deferred (nice-to-have) | ID | Task | Notes | | --------- | ------------------------------- | -------------------------- | | - [ ] F5 | Model comparison (side-by-side) | Needs F4 (chat) first | | - [ ] F10 | Dark/light theme toggle | Low priority, dark is fine | | - [ ] F13 | Responsive mobile layout | Desktop-first tool | | - [ ] F14 | Model tags/labels | After F2 (search) | | - [ ] CQ6 | Zod validation on API responses | After CQ1 (refactor) | | - [ ] F8 | Ollama server logs viewer | Nice but low ROI | | - [ ] S3 | CORS / auth | Local-only, acceptable | --- ## 7. Commit Log _Commits will be added here as work progresses._ | # | Date | Commit | Sprint | Items Completed | | --- | ---- | ------ | ------ | --------------- | | 1 | | | | | --- > **39 items total:** 11 bugs, 6 code quality, 16 features, 5 performance, 3 security > **Estimated total effort:** ~14–17 hours across 5 sprints