- postMessageAndStream data type extended with optional voiceMode field
- startStream signature updated: (userMessage, agentId?, voiceMode?)
- voiceMode forwarded into fetch body via postMessageAndStream call
- VoiceModeToggle: Text / Voice In / Full Voice pills with active/inactive styling
- Auto-play checkbox in full_voice mode, persists to nexus:voice:autoplay in localStorage
- useVoiceMode: reads/writes voiceMode via PATCH /api/nexus/settings with loading state
(deviation Rule 3: created missing blocking dependency for VoiceModeToggle)
- VoiceWaveform: 80x32 canvas with Web Audio AnalyserNode (fftSize=64), 20 animated bars drawn from frequency data using --primary color
- VoiceMicButton: three visual states — idle (Mic icon), recording (VoiceWaveform + ring-2 ring-primary), processing (Loader2 animate-spin)
- All three states have correct aria-labels per UI spec copywriting contract
- Add chatFileRoutes(db, storageService) after assistantHandoffRoutes (inside boardMutationGuard)
- Add nexusSettingsRoutes() after chatFileRoutes
- Extend nexusSettingsSchema with voiceEnabled: z.boolean().default(false)
- Update default return values in nexusSettingsService.get() to include voiceEnabled: false
- Add voiceEnabled?: boolean to NexusSettings client interface in hardware.ts
Replace streamEcho with Puter proxy AI call, inject memory facts as
system message, append memory after each turn. Assistant-to-PM handoff
creates new conversation with context summary.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add OnboardingSummaryStep as step 5 of the wizard
- Add Skip buttons on step 1 (hardware) and step 2 (mode)
- Replace step 4 form submit with Review & finish -> step 5 flow
- Add Skip to summary on step 4
- Step indicator shows 'Summary' on step 5 instead of 'Step 5 of 4'
- Add deriveProviderLabel helper for provider display text
- Add handleStartChat that creates workspace then calls setChatOpen(true)
- Refactor shared workspace creation into createWorkspace() helper
- ProviderSelectionStep: three provider cards (Puter/Google/API key) with adapter badges
- Cards use border-primary bg-primary/5 when selected (matches ModeSelector pattern)
- PuterAuthButton/GoogleOAuthButton/ApiKeyEntryForm wired via callbacks
- NexusOnboardingWizard: step count 3→4, provider selection at step 3
- Parallel probe for hermes_local/claude_local/openclaw_gateway on wizard open
- Credentials stored after company creation (puterToken, googleOAuthStateId, apiKeyData)
- Skip always advances to step 4; Back from step 4 goes to step 3
- Refactor to 3-step flow: hardware detection, mode selection, root directory
- Add step indicator 'Step N of 3'
- Add HardwareSummaryStep on step 1 with dynamic heading
- Add ModeSelector on step 2 with 'both' pre-selected
- Add Back buttons on steps 2 and 3
- Persist selected mode via updateNexusSettings on wizard completion
- Reset step and mode on wizard close
28-02: ollamaApi client, model dropdown in config, skill badge
28-03: stateJson merge after heartbeat, HermesRuntimeCard in AgentOverview
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add ui/src/api/ollama.ts with ollamaApi.status() and ollamaApi.models()
- Replace free-text Model input with hybrid dropdown/fallback in HermesLocalConfigFields
- Dropdown shows pulled Ollama models with * prefix for recommended entries
- Install callout shown when Ollama is absent (with link to installUrl)
- Edit mode: selecting an Ollama model atomically sets model + provider:custom + base_url
- Manual entry fallback via "Other (manual entry)..." option or when Ollama absent
- Uses useCompany() hook for companyId (consistent with AgentConfigForm pattern)
1. Push notifications: call sendPushToAll after streaming completes
2. Mobile offline: add useOfflineQueue + banners to MobileChatView
3. New conversation streaming: call startStream in Path 1 handleSend
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- MobileChatView: full-screen mobile chat using 100dvh, back button, safe-area input
- ChatPanel: conditionally renders MobileChatView on mobile via useMediaQuery
- ChatConversationList: wraps ScrollArea in PullToRefresh for mobile
- ChatInput: pb-[env(safe-area-inset-bottom)] padding + 44px Send button touch target
- ChatConversationItem: min-h-[48px] touch target per UI-SPEC
- useMediaQuery: SSR-safe hook with addEventListener for live breakpoint updates
- usePullToRefresh: touch gesture hook with 64px threshold, haptic feedback via navigator.vibrate
- PullToRefresh: visual wrapper with Loader2 spinner, pull/release text indicators
- Add code category branch in ChatFilePreview routing to ChatCodeFilePreview
- Mark FILE-07 (one-click download) as Complete in REQUIREMENTS.md
- Mark FILE-13 (cross-device access) as Complete in REQUIREMENTS.md
- Update Traceability table for FILE-07 and FILE-13
- Add ChatCodeFilePreview component with hljs syntax highlighting
- Fetch file content from contentPath with credentials
- Use DOMParser-based safe rendering (no dangerouslySetInnerHTML)
- Include copy button, language label, and ChatFileCard download below
- Add extToLang extension-to-language mapping
- Register 14 common languages with hljs
- Add highlight.js as direct dependency in ui/package.json
- Add enableVoiceInput prop to ChatInput props interface
- Add handleTranscription callback that appends transcription text to textarea state
- Render VoiceRecordButton conditionally when enableVoiceInput is true
- Pass enableVoiceInput={true} from ChatPanel to ChatInput
- Mark INPUT-02, INPUT-03, INPUT-04 as Complete in REQUIREMENTS.md traceability table
- Add VoiceRecordButton with MediaRecorder API, recording/transcribing/idle states
- Add POST /transcribe endpoint to chat-files.ts using execFileAsync (safe, no shell)
- Tries whisper-cpp first, falls back to openai-whisper Python CLI
- Returns 503 with helpful message if whisper is not installed
- Create ChatFileDropZone component with drag-and-drop state and overlay
- Add onFilesPicked/pendingFiles/onRemoveFile props to ChatInput
- Wrap form in ChatFileDropZone for drag-and-drop support
- Add handlePaste for clipboard image paste (clipboardData.files)
- Add Paperclip icon button with hidden file input for file picker
- Show pending file chips above textarea with progress and remove button
- Add tests: renders file attach button, calls onFilesPicked, shows pending chips
- Add uploadFile method to chatApi using XHR for progress tracking
- Add ChatFileUploadResponse to shared type imports
- Create useChatFileUpload hook with PendingFile lifecycle management
- Hook manages uploading/done/error states with progress callbacks
- Add onBookmark/isBookmarked props to ChatMessageActions
- Render ChatMessageBookmark as last action for user and assistant messages
- Add onBookmark/isBookmarked props to ChatMessage, thread to ChatMessageActions
- System messages do not receive bookmark actions
- Add scrollToMessageId/setScrollToMessageId to ChatPanelContext
- Add "Search chat messages" item to CommandPalette (dispatches nexus:open-chat-search)
- Integrate ChatSearchDialog, ChatBranchSelector, ChatBookmarkList into ChatPanel
- Add export buttons (Markdown) and bookmarks panel toggle in header
- Wire branch-on-edit: branchConversation called when editing message with subsequent replies
- Add scroll-to-message support in ChatMessageList via virtualizer.scrollToIndex
- Show GitBranch icon for branch conversations in ChatConversationList
- ChatSearchDialog: CommandDialog with shouldFilter=false for server-side FTS, term highlighting via React components
- ChatMessageBookmark: ghost icon button with fill-current for bookmarked state, aria-label toggle
- ChatBookmarkList: scrollable list with skeleton loading, empty state, navigation callbacks
- ChatBranchSelector: horizontal branch picker bar with GitBranch icon, active branch highlight
- ChatTaskCreatedBadge renders loading state and resolved badge with View task link
- ChatStatusUpdateBadge renders CheckCircle2 + agent completion text with task link
- useBrainstormerDefault returns general role agent ID with React Query deduplication
- Fix ChatMessageList synthetic streaming entry to include messageType: null
- ChatSpecCard renders 4-field spec (What/Why/Constraints/Success) with edit mode
- ChatSpecCard action row: Send to PM, Edit, Save as Draft buttons
- ChatSpecCard edit mode with aria-labels, Escape key discard, tab order
- ChatHandoffIndicator separator-style with flanking hr and aria-label
- ChatPanel integrates useStreamingChat, ChatAgentSelector, ChatStopButton
- Agent routing via resolveAgentFromContent for slash commands and @mentions
- handleEdit: editMessage + truncateMessagesAfter + re-stream with edited content
- handleRetry: finds actual prior user message, truncates from user message onward, re-streams
- Build agentMap from agents for message identity bars
- ChatInput: slash command popover (triggered by / at start of input)
- ChatInput: @mention popover (triggered by @word pattern)
- Input disabled during streaming with 'Waiting for response...' placeholder
- Stop button shown conditionally when isStreaming
- Agent selector in header for per-conversation agent switching
- Remove ScrollArea wrapper (replaced by virtualizer's own scroll in ChatMessageList)
- Create ChatMentionPopover (200px, opens upward, agent icon + name + role label)
- Agents filtered by query, max 5 visible, No agents found empty state
- 3 skeleton rows loading state, onOpenAutoFocus prevented for textarea focus
- Include agent-role-colors dependency for worktree build