inline-chat-kit 0.56.0 → 0.58.0
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/CHANGELOG.md +70 -0
- package/README.md +147 -2
- package/dist/Artifact/ChatLayout.d.ts +14 -1
- package/dist/ChatExperience/ChatExperience.d.ts +70 -6
- package/dist/{ChatExperience-DCta8bv3.js → ChatExperience-CrWU56fi.js} +780 -696
- package/dist/ChatExperience-CrWU56fi.js.map +1 -0
- package/dist/ChatInput/ChatInput.d.ts +10 -0
- package/dist/ChatTurnRow/ChatTurnRow.d.ts +22 -5
- package/dist/Conversation/Conversation.d.ts +11 -0
- package/dist/TextHighlighter/TextHighlighter.d.ts +12 -1
- package/dist/demo/ChatExperienceDemo.d.ts +4 -1
- package/dist/demo.js +152 -140
- package/dist/demo.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/inline-chat-kit.css +1 -1
- package/dist/inline-chat-kit.js +1 -1
- package/dist/turnParts/turnParts.d.ts +12 -0
- package/dist/useChatTurns/useChatTurns.d.ts +14 -1
- package/package.json +1 -1
- package/dist/ChatExperience-DCta8bv3.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,76 @@ The versions before 1.0 follow the pre-release convention: **a breaking change
|
|
|
8
8
|
or new public API bumps the minor**, and the patch is for fixes. Anything that would break an
|
|
9
9
|
existing install is called out under **Breaking**, with what to do about it.
|
|
10
10
|
|
|
11
|
+
## 0.58.0 — 2026-09-23
|
|
12
|
+
|
|
13
|
+
The composer at the bottom, as an alternative to the inline one — roadmap I2.
|
|
14
|
+
One prop; nothing changes for a host that does not pass it.
|
|
15
|
+
|
|
16
|
+
### Added
|
|
17
|
+
|
|
18
|
+
- **`composer="inline" | "docked"` on `ChatExperience`.** `"docked"` keeps the
|
|
19
|
+
input at the bottom edge and stacks the conversation above it. The live
|
|
20
|
+
input is still the last turn, so a sent message still becomes its bubble —
|
|
21
|
+
the same element, carried up into the conversation by its layout animation
|
|
22
|
+
as a fresh composer takes its place. A sent message is still brought to the
|
|
23
|
+
top of the view and held there while its answer is written, as it is inline;
|
|
24
|
+
the composer stays on the bottom edge while that happens, and the workspace
|
|
25
|
+
gives up whatever a phone's keyboard covers.
|
|
26
|
+
- **`nextTurn: "after-answer" | "at-send"` on `useChatTurns`.** `"at-send"`
|
|
27
|
+
opens the next input the moment a message goes, so it can be typed into
|
|
28
|
+
while the answer arrives. A send while one is in flight is now refused in
|
|
29
|
+
either mode: it used to start a second run and take the first one's abort
|
|
30
|
+
away.
|
|
31
|
+
- **`busy` on `ChatInput` and `ChatTurnRow`.** Open to type into, closed to
|
|
32
|
+
send from: Enter does nothing, the send glyph is a stop when there is an
|
|
33
|
+
`onStop` and inert when there is not.
|
|
34
|
+
- **`dock` on `Conversation`.** The last child is pinned to the bottom of the
|
|
35
|
+
view — an auto margin while the content is short, `sticky` once it is long —
|
|
36
|
+
and the room an anchor needs is put above it rather than below, where
|
|
37
|
+
`sticky` could not hold it.
|
|
38
|
+
|
|
39
|
+
- **`highlights` and `bookmarks` on `ChatExperience`, `highlights` on
|
|
40
|
+
`ChatTurnRow`, `marking` on `TextHighlighter`.** `highlights={false}` draws
|
|
41
|
+
answers as prose — no marker layer, nothing over the text in the tab order,
|
|
42
|
+
no highlight menu — and takes the saved highlights and the selection-mode
|
|
43
|
+
pair with it. `bookmarks={false}` keeps marking and drops the keeping. Both
|
|
44
|
+
are decided in code rather than offered in the interface, which is also how
|
|
45
|
+
`composer`, `pane`, `surface`, `headerActions` and `composerMenu` work.
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- **A responding bubble drew a stop with nothing behind it** when the host
|
|
50
|
+
had not passed `onStop`. It is drawn only with somewhere to report to.
|
|
51
|
+
|
|
52
|
+
## 0.57.0 — 2026-09-22
|
|
53
|
+
|
|
54
|
+
A pane the host draws itself. `ChatExperience` placed its artifact pane beside
|
|
55
|
+
the conversation and held which one was open, which is right until the host has
|
|
56
|
+
a layout of its own. Nothing changes for a host that passes none of this.
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- **`openArtifactId` and `onOpenArtifactChange` on `ChatExperience`.** Which
|
|
61
|
+
artifact is open, held by the host. Left out, the component keeps its own;
|
|
62
|
+
`null` is a held "none open". The change callback fires either way.
|
|
63
|
+
- **`pane="inline" | "none"` on `ChatExperience`.** `"none"` draws no
|
|
64
|
+
`ArtifactPane`, reserves no width for one and never calls `artifact`; a
|
|
65
|
+
pressed card only reports its id, and still shows it is open from
|
|
66
|
+
`openArtifactId`.
|
|
67
|
+
- **`openArtifact(id)` and `closeArtifact()` in `renderPart`'s context**, next
|
|
68
|
+
to `turnId` — so a host's own card can open the pane. `CustomPartContext` is
|
|
69
|
+
exported. `ChatTurnRow` takes `onArtifactChange` for the same when assembled
|
|
70
|
+
by hand.
|
|
71
|
+
- **`ArtifactPane` outside `ChatLayout`**, documented and tested: a region with
|
|
72
|
+
focus on its title when it mounts, no trap and no Escape unless `modal`.
|
|
73
|
+
- **`surface="flush" | "panes"` on `ChatExperience` and `ChatLayout`.**
|
|
74
|
+
`"panes"` makes the conversation a card of its own, so the pane beside it —
|
|
75
|
+
the kit's or the host's — is a second surface rather than a strip cut off the
|
|
76
|
+
same one. Both are drawn from the same tokens, and `--ick-chat-pane-inset` is
|
|
77
|
+
both the ring of space around them and the gap between them. Below the
|
|
78
|
+
layout's breakpoint the corners and the inset go, where the pane is a sheet.
|
|
79
|
+
`"flush"` is the default and is what the kit drew before.
|
|
80
|
+
|
|
11
81
|
## 0.56.0 — 2026-09-22
|
|
12
82
|
|
|
13
83
|
Four things that kept the kit out of a real host app: an answer could only
|
package/README.md
CHANGED
|
@@ -216,6 +216,7 @@ input that morphs into its own bubble rather than a record of what was typed.
|
|
|
216
216
|
| `onDraft` | `(id, value) => void` | | |
|
|
217
217
|
| `onSubmit` | `(id, value) => void` | | |
|
|
218
218
|
| `onStop` | `() => void` | | |
|
|
219
|
+
| `busy` | `boolean` | | An answer is arriving elsewhere: the composer offers a stop, not a send |
|
|
219
220
|
| `onEdit` | `(id) => void` | | |
|
|
220
221
|
| `onCancelEdit` | `(id) => void` | | |
|
|
221
222
|
| `onCopy` | `(value) => void` | writes to the clipboard | |
|
|
@@ -250,6 +251,53 @@ Every callback is optional; a row with none of them renders and can be marked.
|
|
|
250
251
|
The row carries `id="turn-<id>"` so a host can scroll to one, and `aria-busy`
|
|
251
252
|
while its answer is arriving.
|
|
252
253
|
|
|
254
|
+
### The composer at the bottom
|
|
255
|
+
|
|
256
|
+
The kit's argument is that the input is the message: it stands at the end of
|
|
257
|
+
the conversation and becomes the bubble where you typed it. Some products want
|
|
258
|
+
the shape every other chat has instead — the box at the bottom, the
|
|
259
|
+
conversation stacking above it. That is one prop, not a second kit:
|
|
260
|
+
|
|
261
|
+
```tsx
|
|
262
|
+
<ChatExperience onSend={send} composer="docked" />
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
What changes, and what does not:
|
|
266
|
+
|
|
267
|
+
- **The box stays at the bottom.** `Conversation` pins its last child there —
|
|
268
|
+
pushed down by an auto margin while the conversation is shorter than the
|
|
269
|
+
view, held by `position: sticky` once it is longer. Both put its bottom on
|
|
270
|
+
the same edge, so nothing moves when the conversation crosses from one to
|
|
271
|
+
the other.
|
|
272
|
+
- **It still becomes the bubble.** The live input *is* the last turn, as it
|
|
273
|
+
always was, so the morph is the one the kit already has. What is new is the
|
|
274
|
+
travel: a fresh composer takes the last place, the sent one is no longer
|
|
275
|
+
last and no longer sticky, and its `layout` animation carries it up into the
|
|
276
|
+
conversation where it now belongs.
|
|
277
|
+
- **The next question can be typed while this one is answered.**
|
|
278
|
+
`useChatTurns` opens the next input at send rather than at settle
|
|
279
|
+
(`nextTurn: "at-send"`), the composer is `busy` while the answer arrives —
|
|
280
|
+
Enter does nothing and the send glyph is a stop — and a send while an answer
|
|
281
|
+
is in flight is refused rather than started on top of it. Queueing it is a
|
|
282
|
+
separate thing.
|
|
283
|
+
- **A sent message still goes to the top.** The same anchor the inline
|
|
284
|
+
composer uses: your question is brought to the top of the view and held
|
|
285
|
+
there while its answer is written underneath. The composer does not move
|
|
286
|
+
with it — it is `sticky`, so the room the anchor scrolls into passes behind
|
|
287
|
+
it. The room itself goes *above* the dock rather than below: `sticky` only
|
|
288
|
+
ever pulls an element up, so room underneath would leave the composer
|
|
289
|
+
stranded in the middle of the view.
|
|
290
|
+
- **The keyboard is allowed for.** On a phone the software keyboard comes up
|
|
291
|
+
over the bottom edge; the workspace gives up what `visualViewport` says is
|
|
292
|
+
covered, so the composer rides on the keyboard's top edge.
|
|
293
|
+
- Everything else is the same: the parts, the pane, the highlighter, editing
|
|
294
|
+
a sent message in place, `labels`, `surface="panes"`.
|
|
295
|
+
|
|
296
|
+
With a `chat` of your own, give its hook `nextTurn: "at-send"`. Assembling the
|
|
297
|
+
rows by hand, the same shape is `<Conversation dock>` with the live row last,
|
|
298
|
+
`busy={isStreaming}` on it, and `onStop` on it rather than on the bubble being
|
|
299
|
+
answered.
|
|
300
|
+
|
|
253
301
|
### Two rules everything follows
|
|
254
302
|
|
|
255
303
|
**Surfaces nest in three steps.** A **ground** is what a group of things sits
|
|
@@ -461,6 +509,10 @@ and the kit does not guess its shape.
|
|
|
461
509
|
`ChatExperience` calls `useChatTurns` itself, so a host using it passes its own
|
|
462
510
|
instead — `chat={useChatTurns({ onSend })}` — to hold `updatePart`.
|
|
463
511
|
|
|
512
|
+
The second argument to `renderPart` is `{ turnId, openArtifact, closeArtifact }`
|
|
513
|
+
(`CustomPartContext`), so a card can open the pane — see
|
|
514
|
+
[Drawing the pane yourself](#drawing-the-pane-yourself).
|
|
515
|
+
|
|
464
516
|
#### Citing a source from the prose
|
|
465
517
|
|
|
466
518
|
`[^1]` in the answer is a **citation marker**, and the number is a position in
|
|
@@ -947,6 +999,7 @@ instant the reader scrolls away, with a button offering the way back.
|
|
|
947
999
|
| `scrollButton` | `boolean` | `true` | The way back |
|
|
948
1000
|
| `scrollButtonLabel` | `string` | `"Jump to the latest"` | |
|
|
949
1001
|
| `follow` | `boolean` | `true` | `false` makes it a plain scroll container |
|
|
1002
|
+
| `dock` | `boolean` | `false` | The last child is pinned to the bottom edge — see [The composer at the bottom](#the-composer-at-the-bottom) |
|
|
950
1003
|
| `className` | `string` | | Goes on the root, which is the box you lay out |
|
|
951
1004
|
| `viewportClassName` | `string` | | Goes on the element that scrolls — padding belongs here |
|
|
952
1005
|
|
|
@@ -1028,6 +1081,7 @@ between products, and the only part.
|
|
|
1028
1081
|
| `onOpen` | `(id: string) => void` | Without one the card is a record, not a control |
|
|
1029
1082
|
| `<ArtifactPane>` `modal` | `boolean` | See below |
|
|
1030
1083
|
| `<ChatLayout>` `pane` | `({ narrow }) => ReactNode` | |
|
|
1084
|
+
| `<ChatLayout>` `surface` | `"flush" \| "panes"` | One surface, or two. See below |
|
|
1031
1085
|
|
|
1032
1086
|
**`modal` is the one prop that changes behaviour, and it is not about
|
|
1033
1087
|
position.** Covering the conversation changes what the pane *is*: focus has to
|
|
@@ -1041,6 +1095,78 @@ That is the part worth having in a library rather than the box. On open, focus
|
|
|
1041
1095
|
moves to the pane's heading — not into its first control, which would skip what
|
|
1042
1096
|
the thing is — and it is **not** trapped unless the pane is covering the chat.
|
|
1043
1097
|
|
|
1098
|
+
#### Two panes, not one page split in half
|
|
1099
|
+
|
|
1100
|
+
By default the conversation fills the layout and the pane is the only card on
|
|
1101
|
+
it — which is what a chat that owns the whole window wants. `surface="panes"`
|
|
1102
|
+
makes the conversation a card of its own instead:
|
|
1103
|
+
|
|
1104
|
+
```tsx
|
|
1105
|
+
<ChatExperience onSend={send} surface="panes" artifact={artifact} />
|
|
1106
|
+
```
|
|
1107
|
+
|
|
1108
|
+
Both surfaces are drawn from the same tokens — `--ick-chat-pane-surface` is
|
|
1109
|
+
`--ick-artifact-pane-surface`, and the radius follows — because two panes side
|
|
1110
|
+
by side have to be the same *kind* of thing, or the chat reads as the
|
|
1111
|
+
background and the pane as a dialog over it. One number,
|
|
1112
|
+
`--ick-chat-pane-inset`, is the ring of space around both and the gap between
|
|
1113
|
+
them, so the frame is even the whole way round. No divider: the gap is what
|
|
1114
|
+
says there are two of them.
|
|
1115
|
+
|
|
1116
|
+
Below `<ChatLayout>`'s breakpoint the corners and the inset go: the pane is a
|
|
1117
|
+
sheet over the conversation there, and a card inside a card with a sheet over
|
|
1118
|
+
both is three surfaces for one screen.
|
|
1119
|
+
|
|
1120
|
+
Inside the card, `--ick-page` is the card — so the header's tint and the fades
|
|
1121
|
+
at both ends end in the colour they are standing on rather than in the ground
|
|
1122
|
+
outside it.
|
|
1123
|
+
|
|
1124
|
+
#### Drawing the pane yourself
|
|
1125
|
+
|
|
1126
|
+
`ChatExperience` places the pane beside the conversation. A host with a layout
|
|
1127
|
+
of its own — its own column, a drawer, a route — holds which artifact is open
|
|
1128
|
+
and tells the kit to keep out of the way:
|
|
1129
|
+
|
|
1130
|
+
```tsx
|
|
1131
|
+
const [openId, setOpenId] = useState<string | null>(null);
|
|
1132
|
+
const open = openId ? findArtifact(openId) : null;
|
|
1133
|
+
|
|
1134
|
+
<div className="workspace">
|
|
1135
|
+
<ChatExperience
|
|
1136
|
+
chat={chat}
|
|
1137
|
+
pane="none" // no pane here, and no room made for one
|
|
1138
|
+
openArtifactId={openId} // the cards still show which one is open
|
|
1139
|
+
onOpenArtifactChange={setOpenId} // a card pressed, or a custom card's openArtifact
|
|
1140
|
+
renderPart={renderPart}
|
|
1141
|
+
/>
|
|
1142
|
+
{open && (
|
|
1143
|
+
<aside className="my-column">
|
|
1144
|
+
<ArtifactPane key={openId} title={open.title} onClose={() => setOpenId(null)}>
|
|
1145
|
+
{open.body}
|
|
1146
|
+
</ArtifactPane>
|
|
1147
|
+
</aside>
|
|
1148
|
+
)}
|
|
1149
|
+
</div>
|
|
1150
|
+
```
|
|
1151
|
+
|
|
1152
|
+
- **`openArtifactId` / `onOpenArtifactChange`** hold the state outside. Left
|
|
1153
|
+
out, `ChatExperience` keeps its own, as before. `null` is a held "nothing
|
|
1154
|
+
open"; only `undefined` means "not held". `onOpenArtifactChange` is called
|
|
1155
|
+
either way, so a host can listen without holding.
|
|
1156
|
+
- **`pane="none"`** draws no `ArtifactPane`, makes no room for one and never
|
|
1157
|
+
calls `artifact`. Pressing a card reports the id (pressing the open one again
|
|
1158
|
+
reports `null`).
|
|
1159
|
+
- **A custom card opens one too.** `renderPart`'s second argument carries
|
|
1160
|
+
`openArtifact(id)` and `closeArtifact()`, next to `turnId`: a caregiver in
|
|
1161
|
+
your card opens her profile the way an `ArtifactCard` opens a plan.
|
|
1162
|
+
- **`surface="panes"` goes with it.** The conversation becomes a card, your
|
|
1163
|
+
pane is the second one, and the two match without your having to copy the
|
|
1164
|
+
kit's radius and shadow.
|
|
1165
|
+
- **`<ArtifactPane>` stands on its own.** Outside `ChatLayout` leave `modal`
|
|
1166
|
+
off: it is a region, focus moves to its title when it mounts, and it neither
|
|
1167
|
+
traps focus nor takes Escape. Key it by the artifact's id, so opening a
|
|
1168
|
+
different one is a new pane — and focus moves to the new title.
|
|
1169
|
+
|
|
1044
1170
|
### `<SystemMessage>`
|
|
1045
1171
|
|
|
1046
1172
|
The conversation saying something about itself — not the reader, not the agent.
|
|
@@ -1359,17 +1485,36 @@ for that one instance.
|
|
|
1359
1485
|
|
|
1360
1486
|
### Turning off what you do not use
|
|
1361
1487
|
|
|
1362
|
-
|
|
1363
|
-
|
|
1488
|
+
Everything here is decided **in code**, not offered in the interface. A
|
|
1489
|
+
product either marks passages or it does not, keeps a theme toggle or does
|
|
1490
|
+
not; a control for choosing is a question nobody asked.
|
|
1364
1491
|
|
|
1365
1492
|
```tsx
|
|
1366
1493
|
<ChatExperience
|
|
1367
1494
|
onSend={send}
|
|
1495
|
+
composer="docked" // or "inline", the default
|
|
1496
|
+
highlights={false} // no marker layer, no highlight menu, no saved ones
|
|
1497
|
+
bookmarks={false} // marking stays, keeping goes
|
|
1368
1498
|
headerActions={false} // or ["theme"], or ["share"]
|
|
1369
1499
|
composerMenu={false} // or your own: [{ id, label, icon?, onSelect }]
|
|
1500
|
+
pane="none" // draw the artifact pane yourself
|
|
1501
|
+
surface="panes" // the conversation as a card of its own
|
|
1370
1502
|
/>
|
|
1371
1503
|
```
|
|
1372
1504
|
|
|
1505
|
+
| Prop | Off means |
|
|
1506
|
+
| --- | --- |
|
|
1507
|
+
| `highlights={false}` | Answers are prose: no marker layer, nothing over the text in the tab order, no highlight menu — and no saved highlights or selection-mode pair, since both are about marking |
|
|
1508
|
+
| `bookmarks={false}` | A passage can still be marked and replied to; nothing is kept. Use `onHighlight` to keep them yourself |
|
|
1509
|
+
| `onThreadReply` omitted | No threads. A thread is opened from a highlight, so `highlights={false}` takes them too |
|
|
1510
|
+
| `headerActions={false}` | No theme toggle, no Share. Your own `actions` are unaffected |
|
|
1511
|
+
| `composerMenu={false}` | No "+" in the composer |
|
|
1512
|
+
| `selectionToggle` omitted | No marker/precise pair in the header (the default) |
|
|
1513
|
+
| `cursor` omitted | No pointer-following cursor (the default) |
|
|
1514
|
+
|
|
1515
|
+
Assembling the rows by hand, the same switch is `highlights` on
|
|
1516
|
+
`<ChatTurnRow>`, which is `marking` on `<TextHighlighter>`.
|
|
1517
|
+
|
|
1373
1518
|
The saved highlights button is not one of the header's actions — it appears
|
|
1374
1519
|
only once there is a highlight, and it is the only way back to them. An entry in
|
|
1375
1520
|
your own menu with id `"attach"` and no `onSelect` opens the file picker.
|
|
@@ -28,6 +28,19 @@ export interface ChatLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
28
28
|
* it out and the sheet has neither, which is a sheet a thumb cannot dismiss.
|
|
29
29
|
*/
|
|
30
30
|
onDismiss?: () => void;
|
|
31
|
+
/**
|
|
32
|
+
* Whether the conversation is a surface of its own.
|
|
33
|
+
*
|
|
34
|
+
* `"flush"` (the default): the conversation fills the layout and the pane is
|
|
35
|
+
* the only card — which is what a chat that owns the whole window wants.
|
|
36
|
+
*
|
|
37
|
+
* `"panes"`: two cards side by side on the page, with the same ring of space
|
|
38
|
+
* around both and the same gap between them. A divider would have said the
|
|
39
|
+
* same thing more cheaply and less truthfully: these are two surfaces, not
|
|
40
|
+
* one surface with a line through it. The pane the host draws itself goes
|
|
41
|
+
* beside a `"panes"` conversation and looks like it belongs there.
|
|
42
|
+
*/
|
|
43
|
+
surface?: "flush" | "panes";
|
|
31
44
|
}
|
|
32
45
|
/**
|
|
33
46
|
* Where the pane goes, decided once.
|
|
@@ -41,4 +54,4 @@ export interface ChatLayoutProps extends HTMLAttributes<HTMLDivElement> {
|
|
|
41
54
|
* What is left open is the part that actually differs — what is *in* the pane.
|
|
42
55
|
* See `ArtifactPane`.
|
|
43
56
|
*/
|
|
44
|
-
export declare function ChatLayout({ children, pane, onDismiss, className, ...rest }: ChatLayoutProps): import("react").JSX.Element;
|
|
57
|
+
export declare function ChatLayout({ children, pane, onDismiss, surface, className, ...rest }: ChatLayoutProps): import("react").JSX.Element;
|
|
@@ -5,7 +5,7 @@ import { UseChatTurnsOptions, UseChatTurnsResult } from '../useChatTurns/useChat
|
|
|
5
5
|
import { FoldMotion } from '../QuestionGroup/QuestionGroup';
|
|
6
6
|
import { InlineAnimConfig } from '../ChatInput/ChatInput';
|
|
7
7
|
import { TranscribeHandler } from '../voice/useVoiceInput';
|
|
8
|
-
import { CustomPart, TurnPartUpdate } from '../turnParts/turnParts';
|
|
8
|
+
import { CustomPart, CustomPartContext, TurnPartUpdate } from '../turnParts/turnParts';
|
|
9
9
|
import { ComposerMenuItem } from '../ChatInput/AddCardsOverlay';
|
|
10
10
|
import { ChatLabels } from '../labels/labels';
|
|
11
11
|
import { Answer } from '../QuestionCard/types';
|
|
@@ -84,9 +84,7 @@ export interface ChatExperienceProps {
|
|
|
84
84
|
* it stable — outside the component or in `useCallback` — or every row
|
|
85
85
|
* re-renders on every frame of an answer arriving.
|
|
86
86
|
*/
|
|
87
|
-
renderPart?: (part: CustomPart, context:
|
|
88
|
-
turnId: string;
|
|
89
|
-
}) => ReactNode;
|
|
87
|
+
renderPart?: (part: CustomPart, context: CustomPartContext) => ReactNode;
|
|
90
88
|
/**
|
|
91
89
|
* Every word the chat says, grouped by the component that says it. Partial:
|
|
92
90
|
* anything left out stays English. Reaches every piece through context, so
|
|
@@ -127,6 +125,48 @@ export interface ChatExperienceProps {
|
|
|
127
125
|
contextBase?: number;
|
|
128
126
|
/** The pane beside the conversation, asked for the artifact that is open. */
|
|
129
127
|
artifact?: (openId: string) => ChatExperienceArtifact | null;
|
|
128
|
+
/**
|
|
129
|
+
* Which artifact is open, held by the host. Left out (`undefined`), this
|
|
130
|
+
* component keeps it. `null` is "none open" — held, just empty.
|
|
131
|
+
*/
|
|
132
|
+
openArtifactId?: string | null;
|
|
133
|
+
/** Somebody opened or closed one — a card pressed, the pane's X, a custom
|
|
134
|
+
card calling `openArtifact`. Called whether or not the state is held. */
|
|
135
|
+
onOpenArtifactChange?: (id: string | null) => void;
|
|
136
|
+
/**
|
|
137
|
+
* Who draws the pane. `"inline"` (the default): this component, beside the
|
|
138
|
+
* conversation, from `artifact`. `"none"`: nobody here — no pane, no room
|
|
139
|
+
* made for one. The cards still open and still show which one is open;
|
|
140
|
+
* the host draws the pane wherever its own layout wants it, usually with
|
|
141
|
+
* `openArtifactId` and `<ArtifactPane>`.
|
|
142
|
+
*/
|
|
143
|
+
pane?: "inline" | "none";
|
|
144
|
+
/**
|
|
145
|
+
* Whether the conversation is a card of its own.
|
|
146
|
+
*
|
|
147
|
+
* `"flush"` (the default) fills the window, as before. `"panes"` makes it a
|
|
148
|
+
* surface on the page, so the pane beside it — the kit's or the host's own —
|
|
149
|
+
* is a second surface rather than a strip cut off the same one. See
|
|
150
|
+
* `ChatLayout`.
|
|
151
|
+
*/
|
|
152
|
+
surface?: "flush" | "panes";
|
|
153
|
+
/**
|
|
154
|
+
* Where the composer lives.
|
|
155
|
+
*
|
|
156
|
+
* `"inline"` (the default): the input is the message. It stands at the end
|
|
157
|
+
* of the conversation, and what you type becomes the bubble where you typed
|
|
158
|
+
* it — the argument this kit makes.
|
|
159
|
+
*
|
|
160
|
+
* `"docked"`: the input stays at the bottom of the view, the way most chats
|
|
161
|
+
* keep it, and the conversation stacks above it. Sent, it still becomes the
|
|
162
|
+
* bubble — the same element, travelling into the conversation — and a fresh
|
|
163
|
+
* one takes its place at the bottom straight away, so the next question can
|
|
164
|
+
* be typed while this one is being answered. Everything else is the same:
|
|
165
|
+
* the parts, the pane, the highlighter, editing in place.
|
|
166
|
+
*
|
|
167
|
+
* With a `chat` of your own, give its `useChatTurns` `nextTurn: "at-send"`.
|
|
168
|
+
*/
|
|
169
|
+
composer?: "inline" | "docked";
|
|
130
170
|
/** The theme, if the host keeps it. Left off, this manages its own and puts
|
|
131
171
|
a toggle in the header; `data-theme` on the root element either way, and
|
|
132
172
|
unset until somebody chooses, so the kit follows the system preference —
|
|
@@ -136,8 +176,32 @@ export interface ChatExperienceProps {
|
|
|
136
176
|
/** The pointer-following cursor, and the rule that hides the real one. Only
|
|
137
177
|
ever where there is a pointer to replace. */
|
|
138
178
|
cursor?: boolean;
|
|
139
|
-
/** The freeform-marker / precise-selection pair in the header.
|
|
179
|
+
/** The freeform-marker / precise-selection pair in the header. Drawn only
|
|
180
|
+
where there is marking to choose a mode for. */
|
|
140
181
|
selectionToggle?: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Whether an answer can be marked at all.
|
|
184
|
+
*
|
|
185
|
+
* On by default: a marker over a passage is how a reader asks about one
|
|
186
|
+
* sentence rather than the whole answer. `false` draws answers as prose —
|
|
187
|
+
* no marker layer, no highlight menu, nothing in the tab order over the
|
|
188
|
+
* text — and takes the saved highlights and the selection-mode pair with
|
|
189
|
+
* it, because both are about marking. A thread is opened from a highlight,
|
|
190
|
+
* so `onThreadReply` has nothing to open it from either.
|
|
191
|
+
*
|
|
192
|
+
* Set in code, not offered in the interface: a product either works this
|
|
193
|
+
* way or it does not.
|
|
194
|
+
*/
|
|
195
|
+
highlights?: boolean;
|
|
196
|
+
/**
|
|
197
|
+
* Whether marked passages are kept.
|
|
198
|
+
*
|
|
199
|
+
* On by default, and only ever visible once there is one: the header grows
|
|
200
|
+
* a button with the count, and it opens the sheet that lists them.
|
|
201
|
+
* `false` keeps marking and threads and drops the keeping — for a host that
|
|
202
|
+
* would rather store them itself, through `onHighlight`.
|
|
203
|
+
*/
|
|
204
|
+
bookmarks?: boolean;
|
|
141
205
|
/** How far below the top edge a sent message comes to rest. Sets the
|
|
142
206
|
conversation's own top padding too — the two have to agree, so one number
|
|
143
207
|
writes both. */
|
|
@@ -154,4 +218,4 @@ export interface ChatExperienceProps {
|
|
|
154
218
|
onDecideApproval?: (write: PartWriter, turnId: string, partId: string, decision: Decision) => void;
|
|
155
219
|
className?: string;
|
|
156
220
|
}
|
|
157
|
-
export declare function ChatExperience({ onSend, chat: hostChat, renderPart, labels, headerActions: builtInActions, composerMenu, onTranscribe, onThreadReply, title: titleProp, backHref, backLabel, actions, placeholder, empty, contextTotal, contextBase, artifact, theme: themeProp, onThemeChange, cursor, selectionToggle, anchorOffset, endOffset, animationConfig, foldMotion, feedDelay, onAnswerQuestion, onEditQuestion, onDecideApproval, className, }: ChatExperienceProps): import("react").JSX.Element;
|
|
221
|
+
export declare function ChatExperience({ onSend, chat: hostChat, renderPart, labels, headerActions: builtInActions, composerMenu, onTranscribe, onThreadReply, title: titleProp, backHref, backLabel, actions, placeholder, empty, contextTotal, contextBase, artifact, openArtifactId: openArtifactProp, onOpenArtifactChange, pane: paneMode, surface, composer, theme: themeProp, onThemeChange, cursor, selectionToggle, highlights: marking, bookmarks, anchorOffset, endOffset, animationConfig, foldMotion, feedDelay, onAnswerQuestion, onEditQuestion, onDecideApproval, className, }: ChatExperienceProps): import("react").JSX.Element;
|