Compare commits
1 Commits
main
...
feature/is
| Author | SHA1 | Date | |
|---|---|---|---|
| a5d732bd29 |
281
docs/web-ui.md
281
docs/web-ui.md
@@ -1,6 +1,6 @@
|
|||||||
# Web UI: how it works
|
# Web UI architecture
|
||||||
|
|
||||||
This document explains the built-in browser UI served by the gateway.
|
This document explains the redesigned built-in browser UI served by the gateway.
|
||||||
|
|
||||||
Source file:
|
Source file:
|
||||||
|
|
||||||
@@ -10,148 +10,173 @@ Source file:
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
The Web UI is a single HTML page returned by `GET /` (when enabled).
|
The web UI is still served from `GET /`, but now uses a reusable app shell:
|
||||||
|
|
||||||
It is intentionally simple:
|
- shared **header + sidebar** layout
|
||||||
|
- responsive navigation (desktop sidebar + mobile sheet)
|
||||||
|
- shadcn-style design tokens and component primitives
|
||||||
|
- light/dark theme toggle with persisted preference
|
||||||
|
- chat + conversations + API guide views
|
||||||
|
|
||||||
- plain HTML/CSS/JS (no framework)
|
The gateway API contract is unchanged. Chat requests still use `/v1/chat/stream`.
|
||||||
- sends requests to `/v1/chat/stream`
|
|
||||||
- renders streamed assistant text in real time
|
|
||||||
- stores and reuses `conversationId` in `localStorage`
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Availability
|
## App shell and routes
|
||||||
|
|
||||||
The UI route is controlled by `GATEWAY_ENABLE_WEB_UI`:
|
The page is a small client-side multi-view app with three route panels:
|
||||||
|
|
||||||
- `true` (default): `GET /` returns UI
|
- `#/chat` (default)
|
||||||
|
- `#/conversations`
|
||||||
|
- `#/api`
|
||||||
|
|
||||||
|
Routing is hash-based and keeps the shell consistent across views:
|
||||||
|
|
||||||
|
- **Sidebar**: primary navigation/actions
|
||||||
|
- **Top header**: current page title, status badge, theme toggle, mobile menu trigger
|
||||||
|
- **Main content**: route panel content
|
||||||
|
|
||||||
|
### Shared shell behavior
|
||||||
|
|
||||||
|
- Desktop (`>960px`): sidebar is always visible
|
||||||
|
- Mobile (`<=960px`): sidebar is replaced with a sheet/drawer opened from header
|
||||||
|
- Escape key and backdrop click close the mobile sheet
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## UI primitives and design tokens
|
||||||
|
|
||||||
|
The redesigned UI follows shadcn-style structure and naming:
|
||||||
|
|
||||||
|
- card surfaces
|
||||||
|
- buttons (`default`, `secondary`, `outline`, `ghost`)
|
||||||
|
- inputs and textarea
|
||||||
|
- badge/status pill
|
||||||
|
- separator
|
||||||
|
- sheet/drawer-style mobile nav
|
||||||
|
|
||||||
|
Design tokens are centralized as CSS variables on `:root` and overridden for dark mode via `[data-theme="dark"]`.
|
||||||
|
|
||||||
|
Key token families:
|
||||||
|
|
||||||
|
- `--background`, `--foreground`
|
||||||
|
- `--card`, `--card-foreground`
|
||||||
|
- `--primary`, `--secondary`, `--accent`
|
||||||
|
- `--muted`, `--muted-foreground`
|
||||||
|
- `--border`, `--input`, `--ring`
|
||||||
|
- sidebar-specific tokens
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Theme system (light/dark)
|
||||||
|
|
||||||
|
### Behavior
|
||||||
|
|
||||||
|
- Initial theme defaults to system preference (`prefers-color-scheme`) if no saved preference exists.
|
||||||
|
- User can toggle theme from the header button.
|
||||||
|
- Selected theme is persisted in `localStorage` under:
|
||||||
|
- `pi_gateway_theme`
|
||||||
|
- Theme applies by setting:
|
||||||
|
- `document.documentElement.dataset.theme`
|
||||||
|
- `document.documentElement.style.colorScheme`
|
||||||
|
|
||||||
|
### Persistence and system changes
|
||||||
|
|
||||||
|
- If user has explicitly chosen a theme, that preference wins.
|
||||||
|
- If no explicit preference exists, system theme changes are applied live.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Chat view
|
||||||
|
|
||||||
|
The Chat panel contains:
|
||||||
|
|
||||||
|
1. **Session settings card**
|
||||||
|
- Conversation ID input (`#conversationId`)
|
||||||
|
- optional bearer token input (`#token`)
|
||||||
|
- new session and refresh conversations actions
|
||||||
|
2. **Messages card**
|
||||||
|
- streaming message log (`#messages`)
|
||||||
|
- clear visible messages action
|
||||||
|
3. **Composer card**
|
||||||
|
- prompt textarea (`#message`)
|
||||||
|
- send action (`#send`)
|
||||||
|
- keyboard shortcut hint
|
||||||
|
|
||||||
|
### Streaming behavior
|
||||||
|
|
||||||
|
- Sends `POST /v1/chat/stream`
|
||||||
|
- Parses SSE incrementally
|
||||||
|
- Handles key events:
|
||||||
|
- `assistant_text_delta`
|
||||||
|
- `assistant_thinking_delta`
|
||||||
|
- `tool_start`
|
||||||
|
- `done`
|
||||||
|
- `error`
|
||||||
|
- Saves `conversationId` from `done` into:
|
||||||
|
- input field
|
||||||
|
- localStorage key `pi_gateway_conversation_id`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Conversations view
|
||||||
|
|
||||||
|
The Conversations panel reads `GET /v1/conversations` and renders:
|
||||||
|
|
||||||
|
- conversation id
|
||||||
|
- created/updated timestamps
|
||||||
|
- loaded/streaming indicators
|
||||||
|
|
||||||
|
Per-conversation actions:
|
||||||
|
|
||||||
|
- **Use in chat**: sets `#conversationId`, persists it, routes back to chat
|
||||||
|
- **Delete**: calls `DELETE /v1/conversations/:id`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API guide view
|
||||||
|
|
||||||
|
A lightweight route that summarizes core gateway endpoints used by adapters and the web UI:
|
||||||
|
|
||||||
|
- `/v1/chat/stream`
|
||||||
|
- `/v1/chat`
|
||||||
|
- `/v1/conversations`
|
||||||
|
- `/v1/adapters/chat/stream`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Accessibility notes
|
||||||
|
|
||||||
|
The redesigned UI includes:
|
||||||
|
|
||||||
|
- keyboard-focus-visible styles using `--ring`
|
||||||
|
- semantic regions (`nav`, `header`, `main`, `section`)
|
||||||
|
- `aria-current` on active nav item
|
||||||
|
- mobile sheet dialog semantics (`role="dialog"`, `aria-modal="true"`)
|
||||||
|
- message log with `role="log"` + `aria-live="polite"`
|
||||||
|
- keyboard send shortcut (`Cmd/Ctrl + Enter`)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Availability and auth
|
||||||
|
|
||||||
|
The route is controlled by `GATEWAY_ENABLE_WEB_UI`:
|
||||||
|
|
||||||
|
- `true` (default): `GET /` serves UI
|
||||||
- `false`: `GET /` returns `404` with `{ "error": "Web UI disabled" }`
|
- `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.
|
If `GATEWAY_AUTH_TOKEN` is set, auth is still global, so `GET /` also requires bearer auth.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## UI sections
|
## Storage keys used by the UI
|
||||||
|
|
||||||
### 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`
|
- `pi_gateway_conversation_id`
|
||||||
|
- `pi_gateway_theme`
|
||||||
On load, if this key exists, it pre-fills the conversation input.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Send flow
|
## Related docs
|
||||||
|
|
||||||
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`
|
- `docs/gateway.md`
|
||||||
|
- `docs/channels.md`
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user