Skip to content

Chat Workspace

One chat box is the creation tool for everything: ask a question, run deep research, generate a presentation, prep for an exam. A mode chip row above the composer switches the AI's behavior, and generated artifacts appear inline as rich cards that get organized in a tabbed sidebar and full-width library pages. It serves trainers, learners, and B2C users.

How it works

Two structural rules drive the whole surface:

  • Deselected = chat mode. There is no "Chat" chip. Chat is the subtraction default.
  • Available modes are server-decided. /auth/me returns available_modes, a capability-filtered list of mode IDs. The frontend only maps IDs to chip metadata; getModesByIds(ids) silently drops unknown IDs (deploy-safe). Lock state for B2C free users is layered on top.
Chat (/new-chat: always a fresh composer)
├── Mode chip row (above composer): all available modes inline, flex-wrap, no "More" dropdown
│   └── from /auth/me available_modes → getModesByIds → chip metadata
├── Deselected = chat mode (no "Chat" chip)
├── Generated artifacts appear INLINE as rich cards:
│   ├── PresentationCard → "Open in Editor" → /presentations/:id
│   └── ResearchSessionViewer → Publish as Lesson / Download DOCX / Download MD
├── Right sidebar (tabbed): [Chats] [Research] [Slides], each with "View all →"
└── Library pages (full-width grids): /research, /presentations

History (/history: "Search Chats")
└── Searchable, date-grouped conversation list (groupConversationsByDate)

Key invariant: creation happens in chat; browsing happens in the sidebar (recent items) or library pages (full history). Research opens inline (replaces the chat area, Back returns to chat). Presentations open as a separate route, /presentations/:id, which owns fullscreen Present mode, PPTX/PDF export, and metadata editing that need full-page real estate.

Modes, chips, and freemium locks

Role decides which chips exist; plan and quota decide which are locked. A plan can never add a chip; a role can never cause a lock.

  • Mode definitions live in services/ai-tutor/index.ts: KWILO_MODES (trainer/creator) and KWILO_STUDENT_MODES (learner: explain, practice, doubt, revision, exam_prep). ALL_KWILO_MODES is the union.
  • Chip row renders for all roles whenever modes.length > 0. All chips are inline with flex-wrap.
  • B2C lock policy lives in one lockedModes memo in b2c/ChatPage/index.tsx:
  • Hard locks on Pro-tier modes (question_bank, lesson_plan): not available on free at all.
  • Soft locks on quota-limited modes (presentation, research, exam_prep): flip to locked when quota.remaining <= 0.
  • Downloads are gated at the handler, not just the button. In-chat artifact downloads go through renderDownload / DownloadLockButton (formats include pptx and pdf). PresentationViewerPage reads B2C quota and intercepts pptx/pdf export at the handler, opening the paywall instead. Hiding the button alone would let deep-linked or keyboard paths through.
  • QuotaPill (b2c/components/QuotaPill.tsx) is a button on free plans: clicking anywhere on the pill opens the paywall. Paid plans render it as a static read-out.

New Chat and Search Chats

  • /new-chat always starts a fresh composer. Resuming is explicit, via Search Chats or a /ai-tutor/c/:id deep link. An earlier auto-resume-on-entry mechanism was removed; /new-chat no longer silently reopens the last conversation.
  • /history ("Search Chats") is a searchable, date-grouped list. groupConversationsByDate (HistoryPage/helpers.ts) buckets by date; client-side search filters titles.
  • + New chat lives in the chat header (ChatHeader.tsx), wired to startNewConversation.
  • Nav labels: nav.chat → "New Chat", nav.history → "Search Chats".

Behavior notes

  • Assignments stay on a dedicated page, not in chat. They are stateful (target → distribute → collect → grade → analyze); presentations are one-shot artifacts (generate → view → present → done).
  • Memory v1 reuses UserLearningProfile, no new table. v1 prefill reads most_active_subjects and teaching_patterns via a tiny endpoint.

User flows

Generate a presentation from chat. Open /new-chat. Click the Presentation chip, type "Newton's laws for class 10, 30 minutes", send. A progress bar streams (Outlining → Writing slides → Rendering), then becomes a PresentationCard with title, slide count, badges, and "Open in Editor". Clicking it routes to /presentations/:id. Back returns to chat; the deck also appears in the Slides sidebar tab.

Browse the research library. Click the Research sidebar tab (recent sessions), then "View all →" to /research. Click a card to open /ai-tutor?research=<id>; the chat area is replaced by ResearchSessionViewer (rendered report, source citations, Publish as Lesson / Download DOCX / Download Markdown). Publish opens PublishModal with class/section targeting.

Edge cases

  • First-time trainer (no profile): chips fall back to neutral defaults; no crash.
  • LLM generation failure: progress bar becomes an error card with the message, the original prompt preserved, and Retry (resubmits the exact prompt).
  • Quota exceeded: error card + Upgrade linking to subscription settings.
  • Missing research report_preview: inline bubble shows "Report is ready: download it below" with sources and download buttons.
  • Invalid mermaid syntax: MermaidDiagram calls mermaid.parse() before rendering; bad diagrams show raw code in a warning card.
  • Reopened conversation with a past presentation: loader rebuilds the card from extra_data.presentation_id; falls back to plain text if the deck was deleted.

Routes

Route Component Purpose
/new-chat AITutorPage Always a fresh chat composer
/history HistoryPage "Search Chats": searchable, date-grouped list
/ai-tutor/c/:id AITutorPage Open/continue a specific conversation
/ai-tutor (none) Legacy redirect → Navigate to={ROUTES.newChat}; not a live page
/research ResearchLibraryPage Full-width grid of all research sessions
/presentations PresentationsPage Full-width grid/list of presentations
/presentations/:id PresentationViewerPage HTML viewer: Present, Export, Quick Edit, Improve

Where it lives

  • apps/web/src/services/ai-tutor/index.ts: KWILO_MODES, KWILO_STUDENT_MODES, ALL_KWILO_MODES, getModesByIds, MODE_IDS
  • apps/web/src/pages/b2c/ChatPage/index.tsx: lockedModes memo, download label map
  • apps/web/src/pages/shared/AITutorPage/components/ChatInput/ModeChipRow.tsx: inline chip row
  • apps/web/src/pages/shared/PresentationViewerPage.tsx: intercepts pptx/pdf export for free users
  • apps/backend/src/api/v1/presentations.py + apps/backend/src/api/v1/ai_tutor.py: SSE generation, trainer-context endpoint