HyperSaaS
FrontendChat

Prompt Input

Message input with voice transcription, model switching, and suggestions.

The ChatPromptInput component provides a rich message input with several integrated features.

Core Input

A textarea with keyboard shortcuts:

ShortcutAction
EnterSend message
Shift+EnterNew line
EscapeClear input

The textarea auto-resizes as the user types.

Suggestions

On an empty chat (no messages yet), quick-start suggestions appear above the input:

┌──────────────────────────────────────────┐
│  💡 "Summarize this document"            │
│  💡 "What are the key findings?"         │
│  💡 "Compare these two topics"           │
│  💡 "Explain this concept simply"        │
└──────────────────────────────────────────┘

Clicking a suggestion populates the input and sends immediately.

Voice Input

The mic button enables speech-to-text:

  1. User clicks the mic button
  2. Browser's MediaRecorder API captures audio
  3. On stop, the audio blob is sent to the transcription endpoint:
    POST /api/transcribe-audio/
    Content-Type: multipart/form-data
    { audio_file, workspace_id, language }
  4. Backend processes with OpenAI Whisper
  5. Transcribed text is inserted into the input

Language Selector

A popover dropdown lets the user select the transcription language (defaults to English). This is sent to the Whisper API as a hint for better accuracy.

Model Selector

Users can switch the AI model mid-conversation without opening settings. ModelPicker lists the models from GET /api/workspaces/{id}/ai-models/, grouped by provider with each one's tier; models the workspace's plan doesn't include are locked, naming the plan that does:

┌──────────────────────────────┐
│ Gemini 2.5 Flash         ▾   │
├──────────────────────────────┤
│ OpenAI                       │
│   GPT-5 mini        Fast     │
│   GPT-5          🔒 Pro      │
│ Anthropic                    │
│   Claude Haiku 4.5  Fast     │
│   Claude Opus 4.6 🔒 Team    │
│ Google                       │
│   Gemini 2.5 Flash  Fast   ✓ │
└──────────────────────────────┘

Choosing a model sends a PATCH with the chat's new ai_model; if the backend refuses, its reason shows as a toast. The settings sheet uses the same picker, and the list is fetched once per workspace (useAiModels).

Message Submission

async function handleSubmit(content: string) {
  setIsAiThinking(true);

  const res = await fetch(
    `/api/workspaces/${workspaceId}/chats/${chatId}/messages/`,
    {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ role: "user", content }),
    }
  );

  const data = await res.json();
  // Update message list with user message + AI response
  setIsAiThinking(false);
}

AI Thinking State

While the AI is processing, the UI shows:

  • A typing indicator in the message list
  • The send button is disabled
  • The input is grayed out

Attachments

The prompt input supports file attachments via the AI elements PromptInputProvider:

<PromptInputProvider>
  <PromptInputTextarea placeholder="Type a message..." />
  <PromptInputActions>
    <PromptInputAttachment />
    <PromptInputSubmit />
  </PromptInputActions>
</PromptInputProvider>

Attached files are sent with the message as multipart form data (up to 5, 10 MB each). Images go to the model with that message; documents are read into text the model sees on later turns too. A model that can't read images refuses them with a message.

Stop

While a reply streams, the send button becomes Stop. It posts the stream's id to .../messages/stream/stop/; the reply ends shortly after, and the part written so far is saved.

On this page