Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 0 additions & 96 deletions docs/developers/daemon-client-adapters/tui.md

This file was deleted.

118 changes: 118 additions & 0 deletions docs/developers/daemon-client-adapters/web-ui.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Daemon Web UI Adapter

## Goal

Web chat and web terminal clients should consume `qwen serve` through the
daemon HTTP/SSE APIs and render a client-side transcript. Native local TUI,
channel, and IDE integrations keep their existing default paths for now.

## Shared UI Contract

Use the TypeScript SDK daemon UI exports as the common boundary:

```ts
import {
DaemonClient,
DaemonSessionClient,
createDaemonTranscriptStore,
normalizeDaemonEvent,
} from '@qwen-code/sdk/daemon';
```

The split is:

- `DaemonClient` handles daemon HTTP routes.
- `DaemonSessionClient` owns session creation/attachment and SSE replay.
- `normalizeDaemonEvent()` converts daemon wire events into UI events.
- `createDaemonTranscriptStore()` reduces UI events into transcript blocks.

React clients can use the optional `@qwen-code/webui` binding:

```tsx
import {
DaemonSessionProvider,
useDaemonActions,
useDaemonConnection,
useDaemonPendingPermissions,
useDaemonTranscriptBlocks,
} from '@qwen-code/webui';
```

Minimal React shape:

```tsx
function App() {
return (
<DaemonSessionProvider baseUrl="http://127.0.0.1:4170">
<Transcript />
<PromptBox />
</DaemonSessionProvider>
);
}

function Transcript() {
const blocks = useDaemonTranscriptBlocks();
return blocks.map((block) => <RenderBlock key={block.id} block={block} />);
}
```

The provider creates or attaches a daemon session, subscribes to SSE, keeps the
last event id on `DaemonSessionClient`, and reconnects the stream by default.
Callers can disable that with `autoReconnect={false}` for tests or custom
connection management.

## Browser Deployment Shapes

### Same-Origin Local POC

A daemon-served page can call the daemon directly because the page and API share
one origin. This is the preferred early POC shape for local web chat and web
terminal validation.

### Remote Web Chat / Web Terminal

A production remote web app should normally talk to a backend-for-frontend. The
BFF owns daemon URL, token, workspace routing, and session metadata, then
forwards browser-safe app events to the browser. This keeps bearer tokens out of
browser storage and lets the deployment decide which daemon/workspace a user is
allowed to reach.

### Local Browser Against Local Daemon

A separate local dev server is cross-origin from `qwen serve`; it must either
proxy daemon routes through the same origin or be served by the daemon. The
daemon intentionally rejects arbitrary browser `Origin` requests.

## Rendering Responsibilities

The shared transcript model is semantic, not visual. UI clients decide how to
render:

- user and assistant message blocks
- collapsed thought blocks
- tool status cards
- shell output blocks
- permission request controls
- status/error/debug blocks

The web terminal is a browser-native semantic renderer. It should look and feel
terminal-like with monospace layout, scrollback, prompt input, shortcuts, and
streaming blocks, but it is not a raw PTY proxy and does not require server-side
Ink rendering.

## Merge Safety

- The native `qwen` TUI remains direct and unchanged.
- `--acp`, channel, and IDE paths remain unchanged by default.
- The SDK UI core is additive.
- The WebUI React binding is optional and only runs in clients that import it.
- Removed daemon TUI spike code should not be treated as a product migration.

## Follow-Ups

- Add a daemon-served local `/web` POC or equivalent same-origin web app.
- Build first-class chat and terminal renderers on top of transcript blocks.
- Add richer typed events only where existing daemon events are too low-level
for stable browser UI behavior.
- Consider a dedicated `@qwen-code/daemon-ui-core` package if non-SDK consumers
need the UI core as an independent dependency.
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading