inline-chat-kit 0.58.0 → 0.59.2

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 CHANGED
@@ -8,6 +8,114 @@ 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.59.2 — 2026-09-24
12
+
13
+ ### Fixed
14
+
15
+ Docked, an answer holds still while it arrives and as it finishes. Measured
16
+ after the frame is painted, in four windows, on every answer the demo gives:
17
+ from 6 failures to none.
18
+
19
+ - **An answer with a card in it no longer drops and comes back as it
20
+ settles.** The room under the last turn was a margin written a frame after
21
+ the answer changed. When anything inside the turn got shorter — the
22
+ reasoning folding away as a card arrived — the scroll was clamped before
23
+ the margin caught up, and the whole answer dropped 3–13px for a frame. The
24
+ room is now the last turn's `min-height`, which the browser holds in the
25
+ same layout.
26
+ - **A finished answer is no longer nudged up.** Its row of actions was drawn
27
+ only on settling, 36px of new height at the moment it finished, and an
28
+ answer near the composer was pushed up by what did not fit — 2px, or 25.
29
+ The row is now there, hidden, while the answer arrives.
30
+ - **Following the end of a long answer keeps the last line above the
31
+ composer**, where it went 13px behind it. And when a settled answer does
32
+ have to move, it glides once to where it ends up instead of travelling to
33
+ where the end was and creeping back.
34
+ - **An artifact card grows to its content** instead of jumping from 97px to
35
+ 236px in a frame and throwing the answer under it down.
36
+ - **Inline, the sent bubble no longer flickers between one line and two**
37
+ as its stop leaves: the exit spring overshot zero by a fraction of a pixel,
38
+ enough to wrap, four times in 40ms. It is critically damped now.
39
+ - **Browser scroll anchoring is off in the conversation** — the component
40
+ places the view, and Safari never anchored.
41
+
42
+ ### Added
43
+
44
+ - `npm run still-check` in the gate: no jolt (3px undone within two frames),
45
+ and an answer that fits is not moved as it settles.
46
+
47
+ ### Known
48
+
49
+ - Inline, the room is still the measured margin, and a reasoning folding
50
+ away can still clamp the view for a frame by 3–4px, as it did before.
51
+
52
+ ## 0.59.1 — 2026-09-24
53
+
54
+ ### Fixed
55
+
56
+ - **A sent message goes to the top while the model is still silent.** It
57
+ waited for the answer: a model that thinks for a few seconds and then
58
+ answers all at once left the message where it was sent until the answer
59
+ came. Two causes, either one enough:
60
+ - **A host's chat published a render late.** A host that keeps its
61
+ `useChatTurns` in a store and publishes it from an effect hands the kit the
62
+ turns a render after the press. For that render the turn is still the
63
+ composer, and docked the composer is on the bottom edge — the anchor moved
64
+ the view there and was spent. The anchor now waits for the turn to have
65
+ been sent. Measured before: 399px in a 900px window, 526px in an 844px one.
66
+ - **The room under a docked conversation came up short.** While the
67
+ conversation is shorter than the view, the dock's `margin-top: auto`
68
+ swallows the first of the room, and the correction came a frame after the
69
+ view had set off for the old end — and the view no longer looked. The room
70
+ is now settled in one pass, and a travel that was overtaken is looked at
71
+ again when it lands. Measured before: the second message stopped at 141px
72
+ in a 680px window.
73
+
74
+ ### Added
75
+
76
+ - **`ThinksFirst` and `ChatInAStore` stories**, and `npm run silent-check`
77
+ in the gate: three messages to a model silent for 2.5s, in three windows,
78
+ each at the anchor 700ms after the send and still there at 2300ms. Without
79
+ either fix it fails seven of eighteen.
80
+
81
+ ## 0.59.0 — 2026-09-24
82
+
83
+ From integrating 0.58 into a host app. Nothing changes for a host that passes
84
+ none of the new props, with one visible difference, below.
85
+
86
+ ### Added
87
+
88
+ - **`fill="window" | "container"` on `ChatExperience`.** `"container"` makes
89
+ the chat as tall as its parent instead of the window — both the workspace and
90
+ the page. Embedded in a card inset 12px top and bottom, the window's height
91
+ overran the card by 24px and a docked composer lost half of itself to the
92
+ card's clip. `"window"` is the default and is what the kit did before.
93
+ - **Parts sit in the prose where they arrived.** A part carries `at`, how much
94
+ prose had arrived when it did, stamped by `useChatTurns` on its first
95
+ appearance, and the row draws the answer in that order. "I suggest this
96
+ change:" is drawn before the change it introduces rather than under it. The
97
+ prose is still one string — Copy, the screen reader and versions read `ai` as
98
+ before; only the drawing is cut. Hosts can set `at` themselves; a part without
99
+ one sits before the prose, as every part did.
100
+
101
+ ### Changed
102
+
103
+ - **Prose sent after a part is now drawn after it.** This is the point of the
104
+ above, and it is the one thing an existing host will see: a stream that sent
105
+ text, then a card, then more text used to draw the card on top and all the
106
+ text under it. Streams that send their parts before their prose — the usual
107
+ shape, and every stream in the demo — draw exactly as before. The one story
108
+ that changes is `Localized`, whose sentence now stands above its cards.
109
+ - **The row of actions follows the whole answer.** It lived inside the prose
110
+ block, so an answer made only of parts had no regenerate and no thumbs.
111
+ Copy is offered only when there is prose to copy.
112
+
113
+ ### Fixed
114
+
115
+ - **`highlights={false}` still cut the prose into a span per word.** The spans
116
+ exist for marking — hit-testing, the keyboard cursor, the dimming — and with
117
+ marking off they were 82 elements for four sentences. The answer is text now.
118
+
11
119
  ## 0.58.0 — 2026-09-23
12
120
 
13
121
  The composer at the bottom, as an alternative to the inline one — roadmap I2.
package/README.md CHANGED
@@ -480,6 +480,31 @@ component that owns it.
480
480
  steps that follow from one another. Sending both for one stretch of thinking
481
481
  says it twice.
482
482
 
483
+ #### Where a part sits in the prose
484
+
485
+ The answer is drawn in the order the stream sent it. A part remembers how much
486
+ prose had arrived when it did — `at`, in characters of `ai` — and is drawn at
487
+ that point, so a sentence that introduces a card comes before the card:
488
+
489
+ ```tsx
490
+ yield "I suggest this change:"; // prose
491
+ yield { kind: "custom", id: "change", type: "plan-diff", data }; // at: 22
492
+ yield " Tell me if it looks right."; // prose, after the card
493
+ ```
494
+
495
+ `useChatTurns` stamps `at` on a part's **first** appearance and nothing moves it
496
+ afterwards: an update to a part is not a new part, and a card a host adds with
497
+ `updatePart` after the answer lands at the end. Give `at` yourself to place a
498
+ part — `at: 0` puts it before all the prose. A part with no `at` sits before the
499
+ prose, which is where every part sat before this existed, so turns built by
500
+ hand look as they always have.
501
+
502
+ The prose is still one string: `ai` is what Copy copies, what a screen reader
503
+ hears and what a version keeps. Only the drawing is cut, at the points the
504
+ parts arrived — each run is its own block of markdown, and a highlight cannot
505
+ cross a card. The row of actions goes after whatever the answer ends with, so an
506
+ answer made only of parts has one too, without a Copy of nothing.
507
+
483
508
  #### Your own cards: `custom`
484
509
 
485
510
  Every other kind is a component in this package. `custom` is the host's: `type`
@@ -1499,9 +1524,19 @@ not; a control for choosing is a question nobody asked.
1499
1524
  composerMenu={false} // or your own: [{ id, label, icon?, onSelect }]
1500
1525
  pane="none" // draw the artifact pane yourself
1501
1526
  surface="panes" // the conversation as a card of its own
1527
+ fill="container" // as tall as its parent, not the window
1502
1528
  />
1503
1529
  ```
1504
1530
 
1531
+ **Embedded, use `fill="container"`.** By default the chat is as tall as the
1532
+ window, which is what a page that is nothing but the chat wants. Anywhere
1533
+ smaller — a card in a shell, a sidebar, a split — it has to take its parent's
1534
+ height instead, or it hangs past the bottom of it: measured in a card inset 12px
1535
+ top and bottom, the default overran it by 24px and a docked composer lost half
1536
+ of itself to the card's clip. The parent has to have a height to give. The
1537
+ software keyboard is then the host's to handle, because the chat no longer
1538
+ knows where the window's edge is.
1539
+
1505
1540
  | Prop | Off means |
1506
1541
  | --- | --- |
1507
1542
  | `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 |
@@ -167,6 +167,23 @@ export interface ChatExperienceProps {
167
167
  * With a `chat` of your own, give its `useChatTurns` `nextTurn: "at-send"`.
168
168
  */
169
169
  composer?: "inline" | "docked";
170
+ /**
171
+ * What the chat is as tall as.
172
+ *
173
+ * `"window"` (the default): the whole window, which is what a page that is
174
+ * nothing but the chat wants, and why it needs no height from its host.
175
+ *
176
+ * `"container"`: whatever its parent gives it. For a chat embedded in a
177
+ * card, a sidebar, a split — anywhere smaller than the window, which is
178
+ * nearly every embed. The parent has to have a height to give; the chat
179
+ * fills it and scrolls inside it. Measured in a host whose card was inset
180
+ * 12px top and bottom, `"window"` hung 24px past the card and a docked
181
+ * composer lost half of itself to the card's clip.
182
+ *
183
+ * The software keyboard is the host's to handle here: the chat no longer
184
+ * knows where the window's bottom edge is relative to itself.
185
+ */
186
+ fill?: "window" | "container";
170
187
  /** The theme, if the host keeps it. Left off, this manages its own and puts
171
188
  a toggle in the header; `data-theme` on the root element either way, and
172
189
  unset until somebody chooses, so the kit follows the system preference —
@@ -218,4 +235,4 @@ export interface ChatExperienceProps {
218
235
  onDecideApproval?: (write: PartWriter, turnId: string, partId: string, decision: Decision) => void;
219
236
  className?: string;
220
237
  }
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;
238
+ 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, fill: fillMode, theme: themeProp, onThemeChange, cursor, selectionToggle, highlights: marking, bookmarks, anchorOffset, endOffset, animationConfig, foldMotion, feedDelay, onAnswerQuestion, onEditQuestion, onDecideApproval, className, }: ChatExperienceProps): import("react").JSX.Element;