homelabby/.planning/phases/06-lab-advisor/06-03-SUMMARY.md

5 KiB

phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
06-lab-advisor 03 frontend
advisor
chat
streaming
sse
react
tanstack-query
requires provides affects
06-02
advisor-ui
streaming-chat-frontend
web/src/router.tsx
web/src/components/layout/TopBar.tsx
web/src/components/layout/AppShell.tsx
added patterns
ReadableStream SSE parsing
TanStack Query refetchInterval sidebar
optimistic user messages
streaming cursor animation
created modified
web/src/api/advisor.ts
web/src/pages/AdvisorPage.tsx
web/src/router.tsx
web/src/components/layout/TopBar.tsx
web/src/components/layout/AppShell.tsx
Used fetch + ReadableStream instead of EventSource for SSE — POST body required for chat endpoint
Used a ref (streamingContentRef) alongside state to capture latest streaming content inside async callbacks without stale closure issues
Added noPadding prop to AppShell to support full-height two-panel layout without max-width constraint
Model label in sidebar shows only the model slug (after /) to keep sidebar compact
duration completed tasks_completed files_changed
~8 minutes 2026-04-10T07:39:59Z 2 5

Phase 06 Plan 03: AdvisorPage Frontend Summary

One-liner: Two-panel streaming chat UI at /advisor — sidebar conversation list, fetch+ReadableStream SSE token streaming, model dropdown, ClickHouse dark theme.

Tasks Completed

# Task Commit Files
1 API wrappers for advisor endpoints 811223d web/src/api/advisor.ts
2 AdvisorPage component, route, and TopBar link bcc3608 web/src/pages/AdvisorPage.tsx, web/src/router.tsx, web/src/components/layout/TopBar.tsx, web/src/components/layout/AppShell.tsx

What Was Built

web/src/api/advisor.ts

Typed API wrappers for all three advisor endpoints:

  • fetchConversations() — GET /api/advisor/conversations, returns [] on 404
  • fetchConversation(id) — GET /api/advisor/conversations/:id
  • streamChat(params, onToken, onDone, onError) — POST /api/advisor/chat, reads response.body via getReader() + TextDecoder, splits on \n\n SSE delimiters, parses data: lines, calls onToken per token and onDone on [DONE]

web/src/pages/AdvisorPage.tsx

Full two-panel AdvisorPage:

  • Left sidebar (280px, hidden on mobile): "Lab Advisor" heading, "New Chat" button, conversation list with refetchInterval: 5000, selected state highlighted
  • Main panel: messages area (scrolls to bottom on new content), optimistic user messages (right-aligned bg-[#1a1a1a]), assistant messages (left-aligned bg-[#111]), streaming assistant turn with animated cursor
  • Input row: model <select> (4 OpenRouter models), textarea (Enter to send, Shift+Enter for newline), Send button disabled while streaming or empty
  • State: streamingContentRef ref pattern avoids stale closure in async SSE callbacks while streamingContent state drives render

web/src/router.tsx

Lazy AdvisorPage import + advisorRoute at path /advisor added to route tree.

web/src/components/layout/TopBar.tsx

MessageSquare icon + Advisor Button variant="outline" link inserted between Test and Scan buttons.

web/src/components/layout/AppShell.tsx

Added noPadding?: boolean prop — when true, wraps children in flex-1 flex flex-col overflow-hidden instead of the default padded/max-width container. Required for AdvisorPage's full-viewport two-panel layout.

Deviations from Plan

Auto-fixed Issues

1. [Rule 2 - Missing Feature] AppShell noPadding prop

  • Found during: Task 2
  • Issue: AppShell forced px-4 py-6 max-w-7xl mx-auto padding on all pages. AdvisorPage needs a full-height flex container with no max-width for the two-panel sidebar+chat layout.
  • Fix: Added noPadding?: boolean prop to AppShell. When true, renders flex-1 flex flex-col overflow-hidden wrapper. Backward compatible — all existing pages unaffected.
  • Files modified: web/src/components/layout/AppShell.tsx
  • Commit: bcc3608

Known Stubs

None — all data flows are wired to real backend endpoints from plan 06-02. Conversations and messages load from the API; streaming connects to POST /api/advisor/chat.

Threat Surface Scan

No new network endpoints or auth paths introduced in this plan. All frontend calls go to existing backend endpoints established in 06-02. Assistant message tokens rendered as React text nodes (JSX children) — no dangerouslySetInnerHTML used anywhere. T-06-03-01 (XSS) mitigated.

Self-Check: PASSED

  • web/src/api/advisor.ts — created
  • web/src/pages/AdvisorPage.tsx — created
  • web/src/router.tsx — /advisor route added
  • web/src/components/layout/TopBar.tsx — Advisor link added
  • web/src/components/layout/AppShell.tsx — noPadding prop added
  • Commit 811223d — feat(06-03): add advisor API wrappers
  • Commit bcc3608 — feat(06-03): add AdvisorPage, /advisor route, and TopBar link
  • npm run build — exits 0, AdvisorPage-C_DruhTJ.js 6.72 kB in bundle