@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/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 agentweb
1
+ # Integrating CodexView into AgentWeb
2
2
 
3
- This is a drop-in replacement for `frontend/src/codex/components/MessageBubble.tsx`, `StreamingBubble.tsx`, and `ToolUseBlock.tsx`. The agentweb backend (`backend/src/codex/eventMap.ts`) produces a normalized event stream that is conceptually equivalent to `ChatStreamEvent`, but uses different field names.
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
- ## Adapter required
9
+ ## What you'll wire up
6
10
 
7
- The agentweb backend's `NormalizedEvent` and CodexView's `ChatStreamEvent` are conceptually equivalent but use different field names (e.g., agentweb uses `kind: 'text_delta'`, CodexView uses `type: 'agent_message'`). A small adapter function in agentweb (`frontend/src/codex/adapters/codexview.ts`) is needed to map between them. The Step 3 code example below is illustrative — replace `adapter(stream)` with the adapter-produced array and `stream.connected` with whatever connection-state field your `streamingAtomFamily` actually exposes.
11
+ CodexView consumes a `ChatStreamEvent[]` (one normalized event stream).
12
+ AgentWeb stores chat history in two places:
8
13
 
9
- ## Step 1 install (development)
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
- From the agentweb repo root:
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 file:../CodexView
15
- pnpm --filter frontend add lucide-react
32
+ pnpm --filter frontend add @codexview/react @codexview/adapters lucide-react
16
33
  ```
17
34
 
18
- (Once `codexview` is published to a registry, replace `file:../CodexView` with the version range.)
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
- (Match the variable list in `docs/styling.md` against agentweb's tokens.)
60
+ The full variable list is in [`docs/styling.md`](./styling.md).
41
61
 
42
- ## Step 3 — replace ChatThread internals
62
+ ## Step 3 — bridge history + live stream
43
63
 
44
- `frontend/src/codex/components/ChatThread.tsx`:
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 stream = useAtomValue(streamingAtomFamily(sessionId));
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
- <CodexTranscript
56
- events={adaptStreamEvents(stream)} // replace with adapter-produced ChatStreamEvent[]
57
- status={stream.connected ? undefined : 'stopped'} // replace stream.connected with actual field name
58
- className="aw-codex-transcript"
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
- The adapter maps agentweb's `NormalizedEvent` (with `kind` field) to CodexView's `ChatStreamEvent` (with `type` field). Adjust all property names to match the actual `streamingAtomFamily` shape.
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
- ## Step 4 — clean up + handle approval
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
- Delete from `frontend/src/codex/components/`:
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
- **Keep** approval logic. CodexView v0.1 deliberately does **not** ship an approval-bubble component (see spec §11). If `StreamingBubble.tsx` carried that responsibility, extract the approval portion into a standalone `ApprovalBubble.tsx` component within agentweb and render it next to `<CodexTranscript>` (e.g. as a sibling overlay), feeding it the same approval events.
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
- ## Verify
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
- 2. Confirm streaming text appears with smooth typewriter effect.
87
- 3. Trigger a tool call confirm collapsible result.
88
- 4. Trigger an exec confirm shimmer while running.
89
- 5. Disconnect the network confirm StatusBar shows "已停止" once you set `status="stopped"`.
90
-
91
- ## Rollback
92
-
93
- ```bash
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
- Backend `ChatStreamEvent` shape and SSE endpoints do not change, so revert is purely frontend.
212
+ ## Step 6 adapter contract test (optional but recommended)
98
213
 
99
- ## Type-safety guard
100
-
101
- Add to `frontend/src/codex/types/eventCheck.ts`:
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
- import type { ChatStreamEvent as CV } from 'codexview';
105
- import type { ChatStreamEvent as AW } from '../../../../backend/src/codex/eventMap';
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
- // Will fail to compile if shapes drift.
108
- type _AssertEqual<A, B> = (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2) ? true : never;
109
- type _Check = _AssertEqual<CV, AW>;
260
+ ```bash
261
+ git revert <integration-commit-sha>
110
262
  ```
111
263
 
112
- When the agentweb backend adds an event type, this assertion fails until codexview is updated.
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` | `#1f2328` | primary text |
32
- | `--cv-text-muted` | `#6e7781` | reasoning, captions |
33
- | `--cv-text-inverse` | `#ffffff` | text on user bubble |
34
- | `--cv-bg` | `#ffffff` | transcript background |
35
- | `--cv-bg-raised` | `#f6f8fa` | StatusBar, tool block surface |
36
- | `--cv-bg-user-bubble` | `#2f6feb` | user bubble |
37
- | `--cv-bg-assistant-bubble` | `#f6f8fa` | assistant bubble |
38
- | `--cv-bg-code` | `#0d1117` | exec / code background |
39
- | `--cv-fg-code` | `#e6edf3` | code foreground |
40
- | `--cv-border` | `#d0d7de` | dividers |
41
- | `--cv-axis-color` | `#d0d7de` | turn timeline axis |
42
- | `--cv-shimmer-color` | `rgba(31,35,40,0.08)` | exec shimmer |
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` | `#6e7781` | gray |
49
- | `--cv-status-running` | `#2f6feb` | blue |
50
- | `--cv-status-completed` | `#1a7f37` | green |
51
- | `--cv-status-failed` | `#cf222e` | red |
52
- | `--cv-status-stopped` | `#6e7781` | gray |
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` | `#ddf4e4` |
59
- | `--cv-diff-del-bg` | `#ffebe9` |
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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codexview/react",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },