@codexview/react 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/docs/api.md +43 -5
- package/docs/changelog.md +45 -0
- package/docs/integration-agentweb.md +200 -48
- package/docs/styling.md +49 -19
- package/package.json +1 -1
package/docs/api.md
CHANGED
|
@@ -69,21 +69,39 @@ Returns `null` when `status === 'idle'`.
|
|
|
69
69
|
<ReasoningBlock item={reasoningItem} defaultOpen?={boolean} smoothStream?={boolean} />
|
|
70
70
|
```
|
|
71
71
|
|
|
72
|
+
### `<ToolGroup>` _(since 0.3.0)_
|
|
73
|
+
|
|
74
|
+
```tsx
|
|
75
|
+
<ToolGroup items={consecutiveToolItems} defaultOpen?={boolean}>
|
|
76
|
+
{/* pre-rendered children, one per item, in order */}
|
|
77
|
+
</ToolGroup>
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`<CodexTranscript>` wires this in automatically: each turn's items are partitioned via [`partitionForGrouping`](#partitionforgroupingitems--slice) and consecutive `tool_call` / `exec` / `search` / `patch` / `todo_list` / `raw` items are wrapped in one `<ToolGroup>`. Messages, reasoning, and errors act as break points (errors stay prominent).
|
|
81
|
+
|
|
82
|
+
| Prop | Default | Notes |
|
|
83
|
+
|------|---------|-------|
|
|
84
|
+
| `items` | required | The grouped items, used to compute the summary title. |
|
|
85
|
+
| `children` | required | Pre-rendered child blocks, one per `items[i]`, in order. |
|
|
86
|
+
| `defaultOpen` | `false` | Group starts collapsed. Always opens while any contained item is `pending` / `running`, regardless of this prop. |
|
|
87
|
+
|
|
88
|
+
Title is auto-generated from the item-kind counts, e.g. `更新待办、执行 9 个命令、调用 4 个工具`. Override by passing your own component via `<CodexTranscript components={{ ToolGroup: MyGroup }} />`.
|
|
89
|
+
|
|
72
90
|
### `<ToolCallBlock>`
|
|
73
91
|
|
|
74
92
|
```tsx
|
|
75
|
-
<ToolCallBlock item={toolCallItem} />
|
|
93
|
+
<ToolCallBlock item={toolCallItem} defaultOpen?={boolean} />
|
|
76
94
|
```
|
|
77
95
|
|
|
78
|
-
Renders args inline; result inline if small, collapsed in `<details>` if long (`>500` chars, `>4` lines, or `>3` JSON depth).
|
|
96
|
+
Renders args inline; result inline if small, collapsed in `<details>` if long (`>500` chars, `>4` lines, or `>3` JSON depth). Defaults to collapsed; auto-opens while `pending` / `running`. Header is single-line ellipsized when closed (since 0.2.3).
|
|
79
97
|
|
|
80
98
|
### `<ExecBlock>`
|
|
81
99
|
|
|
82
100
|
```tsx
|
|
83
|
-
<ExecBlock item={execItem} />
|
|
101
|
+
<ExecBlock item={execItem} defaultOpen?={boolean} />
|
|
84
102
|
```
|
|
85
103
|
|
|
86
|
-
Shimmer bar shown while `status === 'running'`. stdout/stderr collapsed beyond size threshold.
|
|
104
|
+
Shimmer bar shown while `status === 'running'`. stdout/stderr collapsed beyond size threshold. Closed header is single-line ellipsized — long absolute paths in `$ ...` commands no longer wrap; opening restores wrap (since 0.2.3).
|
|
87
105
|
|
|
88
106
|
### `<SearchBlock>`
|
|
89
107
|
|
|
@@ -145,12 +163,32 @@ Pure reducer. Use directly to drive your own state container.
|
|
|
145
163
|
|
|
146
164
|
Returns `'idle' | 'working' | 'completed' | 'stopped' | 'failed'`.
|
|
147
165
|
|
|
166
|
+
### `partitionForGrouping(items) => Slice[]` _(since 0.3.0)_
|
|
167
|
+
|
|
168
|
+
Splits a flat `ItemView[]` into slices for `<ToolGroup>` rendering.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
type Slice =
|
|
172
|
+
| { kind: 'single'; item: ItemView } // render as before
|
|
173
|
+
| { kind: 'group'; items: ItemView[] }; // wrap children in <ToolGroup>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
Consecutive groupable kinds (`tool_call`, `exec`, `search`, `patch`, `todo_list`, `raw`) collapse into one `group` slice; non-groupable kinds (`user_message`, `assistant_text`, `reasoning`, `error`) become `single` slices and act as break points.
|
|
177
|
+
|
|
178
|
+
### `summarizeToolGroup(items) => string` _(since 0.3.0)_
|
|
179
|
+
|
|
180
|
+
Returns the human-readable Chinese summary used as a `<ToolGroup>` title — e.g. `更新待办、执行 2 个命令、修改 1 个文件`. Each kind appears once with its count; order is fixed for stable phrasing.
|
|
181
|
+
|
|
182
|
+
### `isGroupableKind(kind) => boolean` _(since 0.3.0)_
|
|
183
|
+
|
|
184
|
+
Returns `true` for the six kinds that get aggregated into a `<ToolGroup>`.
|
|
185
|
+
|
|
148
186
|
### `EMPTY_MODEL`
|
|
149
187
|
|
|
150
188
|
Frozen initial `TranscriptModel`. Always safe to start reducing from this.
|
|
151
189
|
|
|
152
190
|
## Types
|
|
153
191
|
|
|
154
|
-
`ChatStreamEvent`, `ChatStreamEventType`, `TokenUsage`, `SearchResult`, `PatchFile`, `TranscriptModel`, `TurnView`, `ItemView`, `ItemKind`, `ItemStatus`, `TranscriptStatus`, `CodexTranscriptComponents`, `UseCodexTranscriptOptions`, `UseSmoothStreamOptions`, plus per-component prop types (`StatusBarProps`, etc.).
|
|
192
|
+
`ChatStreamEvent`, `ChatStreamEventType`, `TokenUsage`, `SearchResult`, `PatchFile`, `TranscriptModel`, `TurnView`, `ItemView`, `ItemKind`, `ItemStatus`, `TranscriptStatus`, `CodexTranscriptComponents`, `UseCodexTranscriptOptions`, `UseSmoothStreamOptions`, `ToolGroupProps`, `ToolGroupSlice`, plus per-component prop types (`StatusBarProps`, etc.).
|
|
155
193
|
|
|
156
194
|
See [docs/events.md](events.md) for the full `ChatStreamEvent` shape.
|
package/docs/changelog.md
CHANGED
|
@@ -2,6 +2,51 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes documented here. Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
4
4
|
|
|
5
|
+
## [0.3.1] — 2026-05-18
|
|
6
|
+
|
|
7
|
+
### Documentation
|
|
8
|
+
|
|
9
|
+
- `docs/styling.md` token defaults updated to match the new warm-paper light palette (`#faf6ef` / `#3a342c` / `#e8c5b3` / `#2c2620`).
|
|
10
|
+
- `docs/api.md` adds the `<ToolGroup>` reference and the new public exports (`partitionForGrouping`, `summarizeToolGroup`, `isGroupableKind`, `ToolGroupProps`, `ToolGroupSlice`).
|
|
11
|
+
- README highlights the turn-level tool aggregation behavior introduced in 0.3.0.
|
|
12
|
+
|
|
13
|
+
### Notes
|
|
14
|
+
|
|
15
|
+
- No code changes; doc-only release synchronized with the 0.3.0 binary.
|
|
16
|
+
|
|
17
|
+
## [0.3.0] — 2026-05-18
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- New `<ToolGroup>` component aggregates consecutive tool-like items inside a turn (`tool_call`, `exec`, `search`, `patch`, `todo_list`, `raw`) into a single collapsible block. Title is computed from the item-kind counts in Chinese (e.g. `更新待办、执行 9 个命令、调用 4 个工具`).
|
|
22
|
+
- Items that act as natural break points — `user_message`, `assistant_text`, `reasoning`, `error` — remain rendered standalone. Errors stay prominent.
|
|
23
|
+
- Each item inside a group keeps its own `<details>`, so a reader can drill into one call without expanding the whole turn. Groups auto-expand while any contained item is `pending` / `running`.
|
|
24
|
+
- Exports: `ToolGroup`, `ToolGroupProps`, `ToolGroupSlice`, `partitionForGrouping`, `summarizeToolGroup`, `isGroupableKind`.
|
|
25
|
+
- `ToolGroup` added to `CodexTranscriptComponents` — consumers can replace it.
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- `CodexTranscript` now partitions each turn's items via `partitionForGrouping()` before rendering. Single (non-tool) items render exactly as before; consecutive tool items render inside a `<ToolGroup>`.
|
|
30
|
+
|
|
31
|
+
## [0.2.3] — 2026-05-18
|
|
32
|
+
|
|
33
|
+
### Changed
|
|
34
|
+
|
|
35
|
+
- Collapsed `<ExecBlock>` and `<ToolCallBlock>` header rows now constrain their command / title text to one line with `text-overflow: ellipsis`. Long absolute paths in shell commands and long mcp-style tool names no longer let "collapsed" rows wrap into two or three lines. Opening the block restores wrap so the full text is visible.
|
|
36
|
+
|
|
37
|
+
## [0.2.2] — 2026-05-18
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
|
|
41
|
+
- Softer user bubble: `--cv-bg-user-bubble` lightened from terracotta `#b8694a` to peach `#e8c5b3`, with `MessageBubble` switching user-bubble text to `--cv-text` (deep ink brown) for an "ink on warm paper" look. Contrast remains ≥ 7.5:1 (AAA).
|
|
42
|
+
- Collapsed-state visuals for `<ToolCallBlock>`, `<ExecBlock>`, `<TodoListBlock>`, `<RawEventBlock>` are now transparent — only a status-colored left-border and title row are visible. Expanding restores the filled panel (border + raised background). Consecutive tool calls now read as a clean outline rather than stacked color blocks.
|
|
43
|
+
|
|
44
|
+
## [0.2.1] — 2026-05-18
|
|
45
|
+
|
|
46
|
+
### Changed
|
|
47
|
+
|
|
48
|
+
- New default light palette ("warm paper"): ivory page background, deep ink-brown text, terracotta user-bubble accent, sage / vermillion status colors, warm-ink (not cold-black) code blocks. All defaults driven via existing `--cv-*` tokens — no component changes, no new token names. Consumers who relied on the exact previous defaults (`#ffffff` / `#1f2328` / `#2f6feb` / `#0d1117`) can pin them back with a `<style>` block.
|
|
49
|
+
|
|
5
50
|
## [0.2.0] — 2026-05-17
|
|
6
51
|
|
|
7
52
|
### Changed
|
|
@@ -1,31 +1,51 @@
|
|
|
1
|
-
# Integrating CodexView into
|
|
1
|
+
# Integrating CodexView into AgentWeb
|
|
2
2
|
|
|
3
|
-
This
|
|
3
|
+
This guide shows how to replace AgentWeb's hand-rolled chat transcript UI
|
|
4
|
+
(`MessageBubble.tsx`, `StreamingBubble.tsx`, `ToolUseBlock.tsx`) with
|
|
5
|
+
[`@codexview/react`](https://www.npmjs.com/package/@codexview/react)'s
|
|
6
|
+
`<CodexTranscript>`. It targets `@codexview/react@0.2.x` and the current
|
|
7
|
+
AgentWeb data model.
|
|
4
8
|
|
|
5
|
-
##
|
|
9
|
+
## What you'll wire up
|
|
6
10
|
|
|
7
|
-
|
|
11
|
+
CodexView consumes a `ChatStreamEvent[]` (one normalized event stream).
|
|
12
|
+
AgentWeb stores chat history in two places:
|
|
8
13
|
|
|
9
|
-
|
|
14
|
+
- **persisted** — `ChatMessage[]` rows in your database, holding committed
|
|
15
|
+
user / assistant messages, tool calls, and token usage.
|
|
16
|
+
- **live** — `streamingAtomFamily(sessionId)`, holding partial assistant
|
|
17
|
+
text, in-flight tool calls, stream errors, and disconnected / gave-up
|
|
18
|
+
flags.
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
Neither source is `ChatStreamEvent[]` on its own. You need a bridge that
|
|
21
|
+
merges them and synthesizes lifecycle events (`thread_started`,
|
|
22
|
+
`turn_started`, `turn_completed`, `turn_failed`, `turn_aborted`).
|
|
23
|
+
|
|
24
|
+
`@codexview/adapters` ships that bridge as `adaptAgentWebTranscript()` —
|
|
25
|
+
described in [Step 3](#step-3--bridge-history--live-stream) below.
|
|
26
|
+
|
|
27
|
+
## Step 1 — install
|
|
28
|
+
|
|
29
|
+
From the AgentWeb repo root:
|
|
12
30
|
|
|
13
31
|
```bash
|
|
14
|
-
pnpm --filter frontend add
|
|
15
|
-
pnpm --filter frontend add lucide-react
|
|
32
|
+
pnpm --filter frontend add @codexview/react @codexview/adapters lucide-react
|
|
16
33
|
```
|
|
17
34
|
|
|
18
|
-
|
|
35
|
+
`lucide-react` is a peer of `@codexview/react`. Both `@codexview/*`
|
|
36
|
+
packages are ESM-only and require Node ≥ 20 for builds and tests.
|
|
19
37
|
|
|
20
38
|
## Step 2 — load styles + bridge tokens
|
|
21
39
|
|
|
22
40
|
In `frontend/src/main.tsx` (or another global entry):
|
|
23
41
|
|
|
24
42
|
```ts
|
|
25
|
-
import 'codexview/styles.css';
|
|
43
|
+
import '@codexview/react/styles.css';
|
|
26
44
|
```
|
|
27
45
|
|
|
28
|
-
In `frontend/src/codex/styles/tokens.css` (append)
|
|
46
|
+
In `frontend/src/codex/styles/tokens.css` (append), map AgentWeb's design
|
|
47
|
+
tokens onto CodexView's CSS variables so the transcript inherits the
|
|
48
|
+
host theme:
|
|
29
49
|
|
|
30
50
|
```css
|
|
31
51
|
.aw-codex-transcript {
|
|
@@ -37,43 +57,140 @@ In `frontend/src/codex/styles/tokens.css` (append):
|
|
|
37
57
|
}
|
|
38
58
|
```
|
|
39
59
|
|
|
40
|
-
|
|
60
|
+
The full variable list is in [`docs/styling.md`](./styling.md).
|
|
41
61
|
|
|
42
|
-
## Step 3 —
|
|
62
|
+
## Step 3 — bridge history + live stream
|
|
43
63
|
|
|
44
|
-
`
|
|
64
|
+
Use `adaptAgentWebTranscript` to fold both sources into `ChatStreamEvent[]`
|
|
65
|
+
plus a `TranscriptStatus`:
|
|
45
66
|
|
|
46
67
|
```tsx
|
|
68
|
+
// frontend/src/codex/components/ChatThread.tsx
|
|
47
69
|
import { useAtomValue } from 'jotai';
|
|
48
|
-
import { CodexTranscript } from 'codexview';
|
|
70
|
+
import { CodexTranscript } from '@codexview/react';
|
|
71
|
+
import { adaptAgentWebTranscript } from '@codexview/adapters/agentweb-transcript';
|
|
72
|
+
import { useChatMessages } from '../hooks/useChatMessages';
|
|
49
73
|
import { streamingAtomFamily } from '../atoms/streaming';
|
|
50
|
-
import { adaptStreamEvents } from '../adapters/codexview'; // adapter required — see "Adapter required" section above
|
|
51
74
|
|
|
52
75
|
export function ChatThread({ sessionId }: { sessionId: string }) {
|
|
53
|
-
const
|
|
76
|
+
const messages = useChatMessages(sessionId); // ChatMessage[]
|
|
77
|
+
const streaming = useAtomValue(streamingAtomFamily(sessionId));
|
|
78
|
+
|
|
79
|
+
const { events, status, error } = adaptAgentWebTranscript({
|
|
80
|
+
sessionId,
|
|
81
|
+
messages,
|
|
82
|
+
streaming,
|
|
83
|
+
});
|
|
84
|
+
|
|
54
85
|
return (
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
86
|
+
<>
|
|
87
|
+
<CodexTranscript
|
|
88
|
+
events={events}
|
|
89
|
+
status={status}
|
|
90
|
+
className="aw-codex-transcript"
|
|
91
|
+
/>
|
|
92
|
+
{error && <StreamErrorOverlay message={error.message} />}
|
|
93
|
+
</>
|
|
60
94
|
);
|
|
61
95
|
}
|
|
62
96
|
```
|
|
63
97
|
|
|
64
|
-
|
|
98
|
+
### Input shape
|
|
99
|
+
|
|
100
|
+
`adaptAgentWebTranscript` is dependency-free — it accepts structural
|
|
101
|
+
input types so AgentWeb doesn't need to expose internal types upstream.
|
|
102
|
+
Map your row / state shapes onto these fields:
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
interface AgentWebMessage {
|
|
106
|
+
id: string;
|
|
107
|
+
turnId: string; // groups user/assistant rows of one exchange
|
|
108
|
+
role: 'user' | 'assistant';
|
|
109
|
+
text?: string;
|
|
110
|
+
toolCalls?: AgentWebToolCall[];
|
|
111
|
+
usage?: { input?: number; cachedInput?: number; output?: number };
|
|
112
|
+
at?: number; // ms epoch
|
|
113
|
+
}
|
|
65
114
|
|
|
66
|
-
|
|
115
|
+
interface AgentWebToolCall {
|
|
116
|
+
callId: string;
|
|
117
|
+
name: string; // `run_command` → exec_command_*
|
|
118
|
+
args?: unknown; // for run_command: { command: string }
|
|
119
|
+
output?: unknown;
|
|
120
|
+
error?: string;
|
|
121
|
+
completed?: boolean; // false for in-flight live calls
|
|
122
|
+
// run_command extras
|
|
123
|
+
exit?: number;
|
|
124
|
+
stdout?: string;
|
|
125
|
+
stderr?: string;
|
|
126
|
+
durationMs?: number;
|
|
127
|
+
}
|
|
67
128
|
|
|
68
|
-
|
|
129
|
+
interface AgentWebStreamingState {
|
|
130
|
+
turnId?: string;
|
|
131
|
+
partialText?: string;
|
|
132
|
+
partialItemId?: string; // stable React key for the live row
|
|
133
|
+
toolCalls?: AgentWebToolCall[];
|
|
134
|
+
status?: 'streaming' | 'completed' | 'failed' | 'disconnected' | 'gaveUp';
|
|
135
|
+
error?: string;
|
|
136
|
+
usage?: { input?: number; cachedInput?: number; output?: number };
|
|
137
|
+
at?: number;
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
If your `streamingAtomFamily` exposes different field names (e.g.
|
|
142
|
+
`connectionState` instead of `status`), normalize inline at the call
|
|
143
|
+
site — the adapter is intentionally narrow.
|
|
144
|
+
|
|
145
|
+
### What the adapter handles
|
|
146
|
+
|
|
147
|
+
- emits `thread_started` for `sessionId` and synthesizes `turn_started`
|
|
148
|
+
/ `turn_completed` boundaries around each `turnId` group;
|
|
149
|
+
- preserves `turnId` across persisted user message → live partial
|
|
150
|
+
assistant text → final persisted assistant row, so all three render
|
|
151
|
+
in one turn;
|
|
152
|
+
- maps `run_command` calls to `exec_command_begin` + `exec_command_end`,
|
|
153
|
+
everything else to `function_call` + `function_call_output`;
|
|
154
|
+
- maps `{ input, cachedInput, output }` to CodexView's `TokenUsage`,
|
|
155
|
+
aggregating across multiple assistant rows in one turn;
|
|
156
|
+
- converts stream lifecycle into events + status:
|
|
157
|
+
- `streaming` → no terminal event, `status: 'working'`
|
|
158
|
+
- `completed` → `turn_completed`, `status: 'completed'`
|
|
159
|
+
- `failed` → `turn_failed` + `status: 'failed'` + `error.message`
|
|
160
|
+
- `disconnected` / `gaveUp` → `turn_aborted` + `status: 'stopped'`
|
|
161
|
+
- leaves in-flight tool calls (`completed: false`) open — only the
|
|
162
|
+
`_begin` half is emitted, so the call renders with a running
|
|
163
|
+
indicator until the next adapter call sees `completed: true`.
|
|
164
|
+
|
|
165
|
+
## Step 4 — replace transcript rendering, keep host overlays
|
|
166
|
+
|
|
167
|
+
CodexView v0.2 deliberately does **not** ship approval, reconnect, or
|
|
168
|
+
gave-up bubbles — those stay AgentWeb-owned and render next to
|
|
169
|
+
`<CodexTranscript>`, not inside it.
|
|
170
|
+
|
|
171
|
+
Replace **transcript rendering** by removing the imports / renders of:
|
|
69
172
|
|
|
70
173
|
- `MessageBubble.tsx`
|
|
71
174
|
- `StreamingBubble.tsx`
|
|
72
175
|
- `ToolUseBlock.tsx`
|
|
73
176
|
|
|
74
|
-
|
|
177
|
+
…wherever they sit inside the chat transcript. Do **not** delete the
|
|
178
|
+
files unconditionally — if `StreamingBubble.tsx` also rendered the
|
|
179
|
+
approval prompt or "you've been disconnected" banner, extract that
|
|
180
|
+
portion into a sibling component (e.g. `ApprovalOverlay.tsx`,
|
|
181
|
+
`StreamErrorOverlay.tsx`) and render it next to `<CodexTranscript>`.
|
|
182
|
+
|
|
183
|
+
The status returned by the adapter is the contract for those overlays:
|
|
75
184
|
|
|
76
|
-
|
|
185
|
+
| `status` | What to render |
|
|
186
|
+
| ------------ | -------------------------------------------------------------------- |
|
|
187
|
+
| `working` | (nothing — `<CodexTranscript>` shows a running turn) |
|
|
188
|
+
| `completed` | (nothing) |
|
|
189
|
+
| `stopped` | "Disconnected" / "Gave up" banner with a retry / new-turn affordance |
|
|
190
|
+
| `failed` | Error overlay using `error.message` |
|
|
191
|
+
| `idle` | (nothing — empty thread) |
|
|
192
|
+
|
|
193
|
+
## Step 5 — verify
|
|
77
194
|
|
|
78
195
|
```bash
|
|
79
196
|
pnpm --filter frontend test
|
|
@@ -82,31 +199,66 @@ pnpm --filter frontend dev
|
|
|
82
199
|
|
|
83
200
|
In the browser:
|
|
84
201
|
|
|
85
|
-
1. Start a Codex session
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
git revert <integration-commit-sha>
|
|
95
|
-
```
|
|
202
|
+
1. Start a Codex session — confirm the user message + streamed assistant
|
|
203
|
+
reply render under one `TurnContainer`.
|
|
204
|
+
2. Confirm partial assistant text appears with a smooth typewriter
|
|
205
|
+
effect and re-flows when the final persisted row arrives (the
|
|
206
|
+
adapter dedupes by `itemId` if you pass a stable `partialItemId`).
|
|
207
|
+
3. Trigger a tool call — confirm the collapsible result.
|
|
208
|
+
4. Trigger a `run_command` — confirm the shimmer while running.
|
|
209
|
+
5. Disconnect the network — confirm the `<StatusBar>` shows "已停止"
|
|
210
|
+
and your `StreamErrorOverlay` renders.
|
|
96
211
|
|
|
97
|
-
|
|
212
|
+
## Step 6 — adapter contract test (optional but recommended)
|
|
98
213
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
214
|
+
Instead of the old compile-time `_AssertEqual` between AgentWeb and
|
|
215
|
+
CodexView types, test the adapter directly. This catches drift in
|
|
216
|
+
either repo without forcing the two type definitions to match
|
|
217
|
+
character-for-character:
|
|
102
218
|
|
|
103
219
|
```ts
|
|
104
|
-
|
|
105
|
-
import
|
|
220
|
+
// frontend/src/codex/adapters/codexview.test.ts
|
|
221
|
+
import { describe, it, expect } from 'vitest';
|
|
222
|
+
import { adaptAgentWebTranscript } from '@codexview/adapters/agentweb-transcript';
|
|
223
|
+
|
|
224
|
+
describe('AgentWeb → CodexView adapter contract', () => {
|
|
225
|
+
it('renders a two-message turn into a single turn boundary', () => {
|
|
226
|
+
const { events, status } = adaptAgentWebTranscript({
|
|
227
|
+
sessionId: 's1',
|
|
228
|
+
messages: [
|
|
229
|
+
{ id: 'u1', turnId: 't1', role: 'user', text: 'hi' },
|
|
230
|
+
{ id: 'a1', turnId: 't1', role: 'assistant', text: 'hello' },
|
|
231
|
+
],
|
|
232
|
+
now: 1_700_000_000_000,
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
expect(events.map((e) => e.type)).toEqual([
|
|
236
|
+
'thread_started',
|
|
237
|
+
'turn_started',
|
|
238
|
+
'user_message',
|
|
239
|
+
'agent_message',
|
|
240
|
+
'turn_completed',
|
|
241
|
+
]);
|
|
242
|
+
expect(status).toBe('completed');
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
it('surfaces a disconnected stream as stopped + turn_aborted', () => {
|
|
246
|
+
const { events, status } = adaptAgentWebTranscript({
|
|
247
|
+
sessionId: 's1',
|
|
248
|
+
messages: [{ id: 'u1', turnId: 't1', role: 'user', text: 'hi' }],
|
|
249
|
+
streaming: { turnId: 't1', status: 'disconnected' },
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
expect(status).toBe('stopped');
|
|
253
|
+
expect(events.some((e) => e.type === 'turn_aborted')).toBe(true);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
## Rollback
|
|
106
259
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
type _Check = _AssertEqual<CV, AW>;
|
|
260
|
+
```bash
|
|
261
|
+
git revert <integration-commit-sha>
|
|
110
262
|
```
|
|
111
263
|
|
|
112
|
-
|
|
264
|
+
Backend SSE endpoints don't change — revert is purely frontend.
|
package/docs/styling.md
CHANGED
|
@@ -26,37 +26,67 @@ Set these on `.codexview-root` (or any ancestor) to theme.
|
|
|
26
26
|
|
|
27
27
|
### Colors
|
|
28
28
|
|
|
29
|
+
Defaults are the "warm paper" light palette (since 0.2.1): ivory page, deep ink-brown text, peach user-bubble accent, warm-ink code blocks. Override any of these to retheme.
|
|
30
|
+
|
|
29
31
|
| Variable | Default (light) | Notes |
|
|
30
32
|
|----------|-----------------|-------|
|
|
31
|
-
| `--cv-text` | `#
|
|
32
|
-
| `--cv-text-muted` | `#
|
|
33
|
-
| `--cv-text-inverse` | `#
|
|
34
|
-
| `--cv-bg` | `#
|
|
35
|
-
| `--cv-bg-raised` | `#
|
|
36
|
-
| `--cv-bg-user-bubble` | `#
|
|
37
|
-
| `--cv-bg-assistant-bubble` | `#
|
|
38
|
-
| `--cv-bg-code` | `#
|
|
39
|
-
| `--cv-fg-code` | `#
|
|
40
|
-
| `--cv-border` | `#
|
|
41
|
-
| `--cv-axis-color` | `#
|
|
42
|
-
| `--cv-shimmer-color` | `rgba(
|
|
33
|
+
| `--cv-text` | `#3a342c` | primary text (deep ink brown) |
|
|
34
|
+
| `--cv-text-muted` | `#8a7e6b` | reasoning, captions (warm taupe) |
|
|
35
|
+
| `--cv-text-inverse` | `#faf6ef` | text on dark surfaces |
|
|
36
|
+
| `--cv-bg` | `#faf6ef` | transcript background (ivory) |
|
|
37
|
+
| `--cv-bg-raised` | `#f1eadb` | StatusBar, expanded tool block surface |
|
|
38
|
+
| `--cv-bg-user-bubble` | `#e8c5b3` | user bubble (peach; text uses `--cv-text`) |
|
|
39
|
+
| `--cv-bg-assistant-bubble` | `#f1eadb` | assistant bubble |
|
|
40
|
+
| `--cv-bg-code` | `#2c2620` | exec / code background (warm ink) |
|
|
41
|
+
| `--cv-fg-code` | `#ede4d3` | code foreground (cream) |
|
|
42
|
+
| `--cv-border` | `#d9ceb6` | dividers (warm hairline) |
|
|
43
|
+
| `--cv-axis-color` | `#d9ceb6` | turn timeline axis |
|
|
44
|
+
| `--cv-shimmer-color` | `rgba(58,52,44,0.08)` | exec shimmer (warm tint) |
|
|
43
45
|
|
|
44
46
|
### Status colors
|
|
45
47
|
|
|
46
48
|
| Variable | Default | Status |
|
|
47
49
|
|----------|---------|--------|
|
|
48
|
-
| `--cv-status-pending` | `#
|
|
49
|
-
| `--cv-status-running` | `#
|
|
50
|
-
| `--cv-status-completed` | `#
|
|
51
|
-
| `--cv-status-failed` | `#
|
|
52
|
-
| `--cv-status-stopped` | `#
|
|
50
|
+
| `--cv-status-pending` | `#8a7e6b` | warm taupe |
|
|
51
|
+
| `--cv-status-running` | `#5b7c99` | muted slate-blue |
|
|
52
|
+
| `--cv-status-completed` | `#6b8e4e` | sage olive |
|
|
53
|
+
| `--cv-status-failed` | `#b04a3a` | vermillion |
|
|
54
|
+
| `--cv-status-stopped` | `#8a7e6b` | warm taupe |
|
|
53
55
|
|
|
54
56
|
### Diff colors
|
|
55
57
|
|
|
56
58
|
| Variable | Default |
|
|
57
59
|
|----------|---------|
|
|
58
|
-
| `--cv-diff-add-bg` | `#
|
|
59
|
-
| `--cv-diff-del-bg` | `#
|
|
60
|
+
| `--cv-diff-add-bg` | `#e6ecd5` (pale moss) |
|
|
61
|
+
| `--cv-diff-del-bg` | `#f3dcd0` (pale rose) |
|
|
62
|
+
|
|
63
|
+
## Restoring the pre-0.2.1 cool palette
|
|
64
|
+
|
|
65
|
+
Drop these overrides if you preferred the original GitHub-style cool palette:
|
|
66
|
+
|
|
67
|
+
```css
|
|
68
|
+
.codexview-root {
|
|
69
|
+
--cv-text: #1f2328;
|
|
70
|
+
--cv-text-muted: #6e7781;
|
|
71
|
+
--cv-text-inverse: #ffffff;
|
|
72
|
+
--cv-bg: #ffffff;
|
|
73
|
+
--cv-bg-raised: #f6f8fa;
|
|
74
|
+
--cv-bg-user-bubble: #2f6feb;
|
|
75
|
+
--cv-bg-assistant-bubble: #f6f8fa;
|
|
76
|
+
--cv-bg-code: #0d1117;
|
|
77
|
+
--cv-fg-code: #e6edf3;
|
|
78
|
+
--cv-border: #d0d7de;
|
|
79
|
+
--cv-axis-color: #d0d7de;
|
|
80
|
+
--cv-shimmer-color: rgba(31,35,40,0.08);
|
|
81
|
+
--cv-status-running: #2f6feb;
|
|
82
|
+
--cv-status-completed: #1a7f37;
|
|
83
|
+
--cv-status-failed: #cf222e;
|
|
84
|
+
--cv-diff-add-bg: #ddf4e4;
|
|
85
|
+
--cv-diff-del-bg: #ffebe9;
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
You'll also want to override `MessageBubble`'s user-bubble color back to `var(--cv-text-inverse)` via a component swap or a more targeted style, since 0.2.2 switched user-bubble text to `--cv-text`.
|
|
60
90
|
|
|
61
91
|
## Dark theme example
|
|
62
92
|
|