@sarchauhan/chat 0.0.0-stage → 0.0.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 CHANGED
@@ -1,3 +1,131 @@
1
- # Temporary Holding Version
1
+ # @sarchauhan/chat
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An embeddable React chat package with streaming messages, themes, tool views, and interactive widgets. Bring any backend through `ChatAdapter`; Next.js is only used by the example apps.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm install @sarchauhan/chat
9
+ ```
10
+
11
+ React and React DOM (version 18 or later) are peer dependencies and must be installed in your application. The package also depends on the published `@sarchauhan/adapter` and `@sarchauhan/protocol` packages; npm installs these automatically.
12
+
13
+ ## Quick start
14
+
15
+ Create a `ChatAdapter` for your backend, then mount the component and import its stylesheet once:
16
+
17
+ ```tsx
18
+ import { Chat } from "@sarchauhan/chat";
19
+ import type { ChatAdapter } from "@sarchauhan/adapter";
20
+ import "@sarchauhan/chat/styles.css";
21
+
22
+ const adapter: ChatAdapter = {
23
+ async *sendMessage() {
24
+ yield { id: "reply-1", role: "assistant", parts: [{ type: "text", text: "Hello!" }] };
25
+ },
26
+ };
27
+
28
+ export function App() {
29
+ return <Chat adapter={adapter} />;
30
+ }
31
+ ```
32
+
33
+ See the sections below for built-in widgets, adapter payloads, and artifact/subagent rendering.
34
+
35
+ ```tsx
36
+ import { Chat } from "@sarchauhan/chat";
37
+ import "@sarchauhan/chat/styles.css";
38
+
39
+ <Chat adapter={adapter} />;
40
+ ```
41
+
42
+ ## Prebuilt components
43
+
44
+ ```tsx
45
+ import { ReadFileWidget, PatchWidget, SearchWidget, QuestionWidget } from "@sarchauhan/chat/widgets";
46
+
47
+ <div className="chat-root" data-theme="light">
48
+ <ReadFileWidget path="src/config.ts" content="export const retries = 3;" startLine={12} />
49
+ <PatchWidget path="src/config.ts" before="const retries = 1;\n" after="const retries = 3;\n" />
50
+ <SearchWidget query="retries" results={[{ path: "src/config.ts", line: 12, text: "export const retries = 3;" }]} />
51
+ <QuestionWidget
52
+ prompt="How should messages load?"
53
+ options={[{ label: "Stream", value: "stream", description: "Render responses as they arrive.", recommended: true }, "Wait for completion"]}
54
+ allowCustom
55
+ onSubmit={async ({ values, text }) => { await submitAnswer({ values, text }); }}
56
+ />
57
+ </div>;
58
+ ```
59
+
60
+ Import `styles.css` once. Standalone components inherit theme tokens from `.chat-root`. Read and patch views collapse when complete; running/error/approval views open automatically. Previews are bounded, with controls to show more. Diff colors indicate additions/deletions; everything else uses the existing neutral theme. Question supports radio/checkbox choices, descriptions, recommended choices, free text, explicit confirmation, disabled/submitted states, and retry after failed submission.
61
+
62
+ ## Computed tool views
63
+
64
+ `Chat` includes `read-file`, `patch`, `search`, and `question` in its default widget registry. Backends can send them with `createWidgetData(name, props, { id, interactive })` from `@sarchauhan/protocol`.
65
+
66
+ Known tool parts are also mapped automatically:
67
+
68
+ | View | Tool names | Recognized payload |
69
+ | --- | --- | --- |
70
+ | Read file | `read`, `read_file`, `readFile`, `read-file` | Input `path`/`file`/`filename`/`file_path`; output string or `content`/`text`; optional `startLine`/`start_line`/`offset` |
71
+ | Patch | `patch`, `apply_patch`, `edit`, `edit_file` | Unified/apply-patch text in `diff`/`patch`, or `before`/`after`, `old_string`/`new_string`, `oldText`/`newText` |
72
+ | Search | `search`, `grep`, `file_search`, `web_search` | `query`/`pattern`/`search`; output array or `results`/`matches`; result path/file/url, optional line, text/snippet/content, title |
73
+ | Question | `question`, `ask_question` | Input `prompt`/`question`, `options`, optional `description`, `multiple`, `allowCustom` |
74
+
75
+ Unsupported payloads retain the generic tool view. Tool state/error are preserved, and completed question tools cannot be submitted again. A custom registry entry for the original tool name takes precedence; custom entries for canonical widget names override builtins too.
76
+
77
+ Question responses through `Chat` become user messages containing selected labels and custom text. For structured responses with widget IDs, use `WidgetProvider` and its `respondToWidget` callback with `MessageContent`/`WidgetRenderer`. Standalone `QuestionWidget` returns `{ values: string[], text?: string }` directly.
78
+
79
+ Public helpers: `computeToolWidget`, `computeToolState`, `computeDiff`, `computeTextDiff`, `normalizeSearchResults`, and `safeResultUrl`. Computed text diffs use one replacement hunk with three context lines; they preserve unchanged edges and final-newline markers. Supplied unified diffs retain their original hunks and file headers.
80
+
81
+ ## Distribution
82
+
83
+ `npm run build` emits ESM, CommonJS, TypeScript declarations, source maps, and CSS assets. Both JS entry points preserve `"use client"` for React Server Component consumers. React/React DOM are peers; the package does not depend on Next.js or the AI SDK.
84
+
85
+ ## Created files and images
86
+
87
+ Existing protocol `file` parts now render as artifact cards with image thumbnails, preview, open, and download actions. No changes to existing file event producers are needed.
88
+
89
+ Use `ArtifactWidget` for one file and `ArtifactsWidget` for a list. Text previews are escaped text; HTML and SVG are never embedded as active documents. Relative/signed download URLs remain owned by the backend. Raster images, HTTP(S), and blob URLs are supported; unsafe executable URLs are rejected. Browser cross-origin download behavior depends on the backend's Content-Disposition header.
90
+
91
+ For creation states and metadata, use the additive data envelope:
92
+
93
+ ```ts
94
+ import { createArtifactData } from "@sarchauhan/protocol";
95
+ writer.write(createArtifactData({
96
+ id: "report-1", filename: "report.md", mediaType: "text/markdown",
97
+ status: "creating",
98
+ }));
99
+ // Later: same ID, complete snapshot replaces the card in place.
100
+ writer.write(createArtifactData({
101
+ id: "report-1", filename: "report.md", mediaType: "text/markdown",
102
+ status: "ready", url: "/files/report.md",
103
+ content: "# Results\n\nThe adapter is ready.", sizeBytes: 1204,
104
+ }));
105
+ ```
106
+
107
+ Tool outputs with an explicit `{ artifact }` or `{ artifacts: [...] }` envelope also compute these views for otherwise unrecognized tool names. Local filesystem paths are not assumed to be browser-downloadable URLs. The widget registry includes `artifact` and `artifacts`.
108
+
109
+ ## Integrated subagents
110
+
111
+ A subagent appears as a compact activity card in the parent conversation. Its inspection view uses the same accessible dialog on every screen: a centered modal above 640px and a bottom sheet at or below 640px. It supports Escape/backdrop dismissal, focus containment/restoration, reduced motion, safe-area padding, a scrolling activity transcript, nested tool views, and created artifacts. Portal content inherits the host theme and custom chat tokens.
112
+
113
+ ```ts
114
+ import { createSubagentData } from "@sarchauhan/protocol";
115
+ writer.write(createSubagentData({
116
+ id: "session-1:research-1", parentId: "session-1:main",
117
+ title: "Research agent", task: "Compare the storage options",
118
+ state: "running", progress: 0.6,
119
+ messages: [{
120
+ id: "reply-1", role: "assistant",
121
+ parts: [{ type: "text", text: "Checking the adapter contract." }],
122
+ }],
123
+ }));
124
+ // Another full snapshot with the same ID updates the existing card/dialog.
125
+ ```
126
+
127
+ `AgentSubagent` is independent of any harness. Map backend states into `queued`, `running`, `completed`, `failed`, or `cancelled`. IDs identify runs, not just agent names; namespace them across sessions/harnesses. `parentId` is optional correlation metadata. Progress is an optional fraction, not a fabricated estimate. Messages and artifacts are optional; include only the activity intended for users. System messages are excluded from the inspection transcript.
128
+
129
+ The event remains an existing `AgentDataEvent` (`data-subagent`); no new top-level event/part union or transport is introduced. Only `data-subagent` and `data-artifact` events with IDs replace previous snapshots within a message. Other data events retain append semantics. Each snapshot is complete: omitted fields are cleared rather than implicitly merged. Producers own event ordering and persistence; use one parent message per run or replace the prior parent message snapshot when continuing across turns. A harness that does not provide activity still produces a useful status/task card.
130
+
131
+ Use `SubagentWidget` directly for custom layouts, or let `Chat`/`MessageContent` render `data-subagent` through the builtin registry. Multiple run IDs render separate cards. Inspection nesting is bounded to three levels while keeping deeper status cards visible.
@@ -0,0 +1,291 @@
1
+ @keyframes agent-tool-spin {
2
+ to {
3
+ transform: rotate(360deg);
4
+ }
5
+ }
6
+
7
+ .agent-tool,
8
+ .agent-data {
9
+ margin: 0;
10
+ color: inherit;
11
+ }
12
+
13
+ .agent-tool > summary,
14
+ .agent-data > summary {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: var(--chat-space-2);
18
+ padding: var(--chat-space-1_5) 0;
19
+ cursor: pointer;
20
+ list-style: none;
21
+ font-size: var(--chat-fs-14);
22
+ line-height: var(--chat-leading-snug);
23
+ font-weight: var(--chat-weight-regular);
24
+ letter-spacing: var(--chat-tracking-normal);
25
+ opacity: 0.76;
26
+ transition:
27
+ color var(--chat-transition),
28
+ opacity var(--chat-transition);
29
+ }
30
+
31
+ .agent-tool > summary:hover {
32
+ color: var(--chat-fg);
33
+ opacity: 1;
34
+ }
35
+
36
+ .agent-tool > summary::-webkit-details-marker,
37
+ .agent-data > summary::-webkit-details-marker {
38
+ display: none;
39
+ }
40
+
41
+ .agent-tool-dot {
42
+ display: inline-flex;
43
+ align-items: center;
44
+ justify-content: center;
45
+ width: 1rem;
46
+ height: 1rem;
47
+ flex: 0 0 auto;
48
+ color: var(--chat-fg-muted);
49
+ /* Restrain success-check so a 10px glyph does not bob 40px */
50
+ --check-y-amount: 3px;
51
+ --check-blur-from: 2px;
52
+ --check-rotate-from: 24deg;
53
+ --check-opacity-dur: 280ms;
54
+ --check-rotate-dur: 280ms;
55
+ --check-bob-dur: 240ms;
56
+ --check-blur-dur: 240ms;
57
+ --check-path-dur: 280ms;
58
+ --check-path-delay: 40ms;
59
+ }
60
+
61
+ .agent-tool-dot .t-icon-swap {
62
+ width: 1rem;
63
+ height: 1rem;
64
+ place-items: center;
65
+ }
66
+
67
+ .agent-tool-dot .t-success-check {
68
+ display: inline-flex;
69
+ line-height: 0;
70
+ }
71
+
72
+ .agent-tool-chevron {
73
+ width: 0.9rem;
74
+ height: 0.9rem;
75
+ flex: 0 0 auto;
76
+ opacity: 0.58;
77
+ transition: transform var(--chat-transition);
78
+ }
79
+
80
+ .agent-tool[open] .agent-tool-chevron {
81
+ transform: rotate(180deg);
82
+ }
83
+
84
+ .agent-tool-pending .agent-tool-dot {
85
+ color: color-mix(in oklab, var(--chat-warning) 70%, currentColor);
86
+ }
87
+
88
+ .agent-tool-complete .agent-tool-dot {
89
+ color: var(--chat-fg-muted);
90
+ }
91
+
92
+ .agent-tool-spinner {
93
+ width: 0.62rem;
94
+ height: 0.62rem;
95
+ border: 1.5px solid currentColor;
96
+ border-top-color: transparent;
97
+ border-radius: 999px;
98
+ animation: agent-tool-spin 0.8s linear infinite;
99
+ opacity: 0.9;
100
+ }
101
+
102
+ .agent-event-marker {
103
+ width: 0.42rem;
104
+ height: 0.42rem;
105
+ flex: 0 0 auto;
106
+ border-radius: 999px;
107
+ background: currentColor;
108
+ opacity: 0.4;
109
+ }
110
+
111
+ .agent-event .agent-tool-body {
112
+ padding-top: 0.35rem;
113
+ }
114
+
115
+ .agent-data > summary::before {
116
+ content: "";
117
+ width: 0.42rem;
118
+ height: 0.42rem;
119
+ flex: 0 0 auto;
120
+ border-radius: 999px;
121
+ background: currentColor;
122
+ opacity: 0.3;
123
+ transition:
124
+ opacity var(--chat-transition),
125
+ transform var(--chat-transition);
126
+ }
127
+
128
+ .agent-data[open] > summary::before {
129
+ opacity: 0.62;
130
+ transform: scale(0.72);
131
+ }
132
+
133
+ .agent-tool-name {
134
+ min-width: 0;
135
+ overflow: hidden;
136
+ color: inherit;
137
+ font-family: var(--chat-font-sans);
138
+ font-size: var(--chat-text);
139
+ font-weight: var(--chat-weight-medium);
140
+ text-overflow: ellipsis;
141
+ white-space: nowrap;
142
+ }
143
+
144
+ .agent-tool-chip {
145
+ min-width: 0;
146
+ max-width: min(100%, 22rem);
147
+ overflow: hidden;
148
+ color: var(--chat-fg-muted);
149
+ font-family: var(--chat-font-mono);
150
+ font-size: var(--chat-text-sm);
151
+ line-height: var(--chat-leading-snug);
152
+ text-overflow: ellipsis;
153
+ white-space: nowrap;
154
+ }
155
+
156
+ .agent-tool-state {
157
+ margin-left: auto;
158
+ color: var(--chat-fg-muted);
159
+ font-size: var(--chat-text-sm);
160
+ font-weight: var(--chat-weight-medium);
161
+ white-space: nowrap;
162
+ }
163
+
164
+ .agent-tool-pending .agent-tool-state {
165
+ color: color-mix(in oklab, var(--chat-warning) 62%, currentColor);
166
+ }
167
+
168
+ .agent-tool-complete .agent-tool-state {
169
+ color: var(--chat-fg-subtle);
170
+ }
171
+
172
+ .agent-tool-error .agent-tool-dot,
173
+ .agent-tool-error .agent-tool-state {
174
+ color: color-mix(in oklab, var(--chat-danger) 64%, currentColor);
175
+ }
176
+
177
+ .agent-tool-error .agent-tool-state {
178
+ opacity: 0.88;
179
+ }
180
+
181
+ .agent-tool-body,
182
+ .agent-data > pre {
183
+ margin-left: 1.25rem;
184
+ padding: var(--chat-space-2) 0 0;
185
+ }
186
+
187
+ .agent-tool-section + .agent-tool-section {
188
+ margin-top: var(--chat-space-3_5);
189
+ }
190
+
191
+ .agent-tool-section-label {
192
+ margin-bottom: var(--chat-space-1);
193
+ color: var(--chat-fg-muted);
194
+ font-size: var(--chat-text-xs);
195
+ font-weight: var(--chat-weight-medium);
196
+ }
197
+
198
+ .agent-tool-code {
199
+ margin: 0;
200
+ overflow-x: auto;
201
+ padding: 0;
202
+ color: inherit;
203
+ font-family: var(--chat-font-mono);
204
+ font-size: var(--chat-fs-12);
205
+ line-height: var(--chat-leading-normal);
206
+ opacity: 0.72;
207
+ white-space: pre-wrap;
208
+ word-break: break-word;
209
+ }
210
+
211
+ .agent-tool-error {
212
+ color: color-mix(in oklab, var(--chat-danger) 72%, currentColor);
213
+ opacity: 0.92;
214
+ }
215
+
216
+ .agent-source,
217
+ .agent-file,
218
+ .agent-reference {
219
+ display: inline-flex;
220
+ flex-direction: column;
221
+ gap: var(--chat-space-05);
222
+ max-width: min(100%, 32rem);
223
+ padding: var(--chat-space-05) 0;
224
+ color: inherit;
225
+ text-decoration: none;
226
+ }
227
+
228
+ .agent-source:hover,
229
+ .agent-file:hover {
230
+ color: var(--chat-fg);
231
+ }
232
+
233
+ .agent-source-label {
234
+ color: var(--chat-fg-subtle);
235
+ font-size: var(--chat-text-xs);
236
+ font-weight: var(--chat-weight-medium);
237
+ }
238
+
239
+ .agent-source-title {
240
+ overflow: hidden;
241
+ color: inherit;
242
+ font-size: var(--chat-text-sm);
243
+ font-weight: var(--chat-weight-medium);
244
+ line-height: var(--chat-leading-normal);
245
+ text-overflow: ellipsis;
246
+ white-space: nowrap;
247
+ }
248
+
249
+ .agent-source-meta {
250
+ color: inherit;
251
+ font-size: var(--chat-caption-size);
252
+ opacity: 0.46;
253
+ }
254
+
255
+ .agent-file-image {
256
+ display: block;
257
+ overflow: hidden;
258
+ padding: 0;
259
+ background: transparent;
260
+ }
261
+
262
+ .agent-file-image img {
263
+ display: block;
264
+ width: 100%;
265
+ height: auto;
266
+ opacity: 0.92;
267
+ }
268
+
269
+ .agent-file-image figcaption {
270
+ padding: var(--chat-space-2) 0 0;
271
+ color: inherit;
272
+ font-size: var(--chat-caption-size);
273
+ opacity: 0.46;
274
+ }
275
+
276
+ .agent-reference {
277
+ display: flex;
278
+ max-width: 100%;
279
+ padding: var(--chat-space-05) 0;
280
+ }
281
+
282
+ .agent-reference + .agent-reference {
283
+ margin-top: var(--chat-space-2);
284
+ }
285
+
286
+ .agent-reference-body {
287
+ color: inherit;
288
+ font-size: var(--chat-fs-14);
289
+ line-height: var(--chat-leading-normal);
290
+ opacity: 0.72;
291
+ }