All checks were successful
Build and Push Docker Image / build (push) Successful in 1m15s
Reviewed-on: MoA/agent#1 Co-authored-by: highperfocused <highperfocused@pm.me> Co-committed-by: highperfocused <highperfocused@pm.me>
158 lines
3.6 KiB
Markdown
158 lines
3.6 KiB
Markdown
# Web UI: how it works
|
|
|
|
This document explains the built-in browser UI served by the gateway.
|
|
|
|
Source file:
|
|
|
|
- `src/gateway/web-ui.ts`
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
The Web UI is a single HTML page returned by `GET /` (when enabled).
|
|
|
|
It is intentionally simple:
|
|
|
|
- plain HTML/CSS/JS (no framework)
|
|
- sends requests to `/v1/chat/stream`
|
|
- renders streamed assistant text in real time
|
|
- stores and reuses `conversationId` in `localStorage`
|
|
|
|
---
|
|
|
|
## Availability
|
|
|
|
The UI route is controlled by `GATEWAY_ENABLE_WEB_UI`:
|
|
|
|
- `true` (default): `GET /` returns UI
|
|
- `false`: `GET /` returns `404` with `{ "error": "Web UI disabled" }`
|
|
|
|
If `GATEWAY_AUTH_TOKEN` is enabled, `GET /` also requires an `Authorization` header, because auth is global in the gateway.
|
|
|
|
---
|
|
|
|
## UI sections
|
|
|
|
### 1) Session/header card
|
|
|
|
- **Conversation ID input** (`#conversationId`)
|
|
- if empty, server auto-creates one during first message
|
|
- persisted locally under `pi_gateway_conversation_id`
|
|
- **Auth token input** (`#token`)
|
|
- optional bearer token included in API requests from the page
|
|
- this affects `fetch` calls only; it does not add auth headers to the initial page load
|
|
|
|
### 2) Messages card
|
|
|
|
- container `#messages`
|
|
- each message is appended as a `.msg.user` or `.msg.assistant` block
|
|
- text is rendered as plain text (`textContent`), not Markdown/HTML
|
|
|
|
### 3) Composer card
|
|
|
|
- textarea `#message`
|
|
- status text `#status`
|
|
- buttons:
|
|
- `Send`
|
|
- `New session`
|
|
|
|
---
|
|
|
|
## Local state
|
|
|
|
The page keeps only minimal browser-side state:
|
|
|
|
- `conversationId` in input + local storage
|
|
- rendered message list in DOM
|
|
- current request state via button disabled/enabled
|
|
|
|
Storage key:
|
|
|
|
- `pi_gateway_conversation_id`
|
|
|
|
On load, if this key exists, it pre-fills the conversation input.
|
|
|
|
---
|
|
|
|
## Send flow
|
|
|
|
When user presses **Send** (or Cmd/Ctrl + Enter):
|
|
|
|
1. Trim textarea value; ignore empty input.
|
|
2. Disable `Send` and `New session` buttons.
|
|
3. Append user message bubble.
|
|
4. Append empty assistant bubble.
|
|
5. Build payload:
|
|
- required: `message`
|
|
- optional: `conversationId` (if input non-empty)
|
|
6. POST to `/v1/chat/stream` with JSON body.
|
|
7. Parse SSE stream incrementally.
|
|
8. Update assistant bubble and status based on events.
|
|
9. Re-enable buttons when request finishes/fails.
|
|
|
|
---
|
|
|
|
## SSE event handling in UI
|
|
|
|
Handled events:
|
|
|
|
- `assistant_text_delta`
|
|
- appends `data.delta` to assistant message bubble
|
|
- `done`
|
|
- reads `data.conversationId`
|
|
- updates conversation input
|
|
- writes `pi_gateway_conversation_id`
|
|
- status becomes `Done • conversation <id>`
|
|
- `error`
|
|
- status becomes error text
|
|
- writes fallback error into assistant bubble if empty
|
|
|
|
Other event types are currently ignored by the UI.
|
|
|
|
---
|
|
|
|
## New session button behavior
|
|
|
|
Clicking **New session**:
|
|
|
|
- does nothing if a request is currently streaming (`Send` disabled)
|
|
- clears conversation ID input
|
|
- removes `pi_gateway_conversation_id` from local storage
|
|
- clears rendered message list
|
|
- sets status to `New session ready`
|
|
- focuses the message textarea
|
|
|
|
This starts a fresh client-side chat thread. The next send will create a new conversation on the server.
|
|
|
|
---
|
|
|
|
## Keyboard shortcut
|
|
|
|
In the message textarea:
|
|
|
|
- `Cmd + Enter` (macOS) or `Ctrl + Enter` (Windows/Linux)
|
|
- triggers the same send flow as the Send button
|
|
|
|
---
|
|
|
|
## Limitations
|
|
|
|
Current UI is intentionally minimal:
|
|
|
|
- no server-side message history loading
|
|
- no cancel/abort button for in-flight response
|
|
- no rendering for tool events/thinking events
|
|
- no Markdown formatting
|
|
- no multi-conversation sidebar
|
|
|
|
It is best used as a lightweight test/debug interface.
|
|
|
|
---
|
|
|
|
## Related API docs
|
|
|
|
For full gateway/API details, see:
|
|
|
|
- `docs/gateway.md`
|