inline-chat-kit 0.54.0 → 0.54.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
@@ -1,11 +1,97 @@
1
1
  # Changelog
2
2
 
3
- Dates are the day the work landed on `main`.
3
+ Dates are the day the work landed on `main`, which is not always the day it
4
+ reached npm: `0.52.1` and `0.53.0` were written up here and never published,
5
+ and their changes shipped in `0.54.0`. Each is marked.
4
6
 
5
7
  The versions before 1.0 follow the pre-release convention: **a breaking change
6
8
  or new public API bumps the minor**, and the patch is for fixes. Anything that would break an
7
9
  existing install is called out under **Breaking**, with what to do about it.
8
10
 
11
+ ## 0.54.2 — 2026-09-12
12
+
13
+ ### Fixed
14
+
15
+ - **The sent message leapt off the top of the view when an answer settled.**
16
+ Measured on the demo at 1100×700, at the frame the turn count went from one
17
+ to two: `scrollTop` 0 → 175, in a single frame, and the message that had
18
+ just been sent went from 100px down the view to −75.
19
+
20
+ It was not the scroll logic. `Conversation` made **exactly one** correction
21
+ in the whole session — a smooth one, arriving after the jump had already
22
+ happened. The jump was `focus()`: when an answer settles the next turn is
23
+ appended, its composer mounts at the bottom of the conversation and focuses
24
+ itself, and focusing an element brings it into view. Instantly, and without
25
+ asking anyone.
26
+
27
+ `preventScroll` now. Where the view goes belongs to `Conversation`, which
28
+ knows about anchors, tails, and whether the reader has scrolled away; focus
29
+ is about the keyboard. The same moment is now eight frames with a largest
30
+ step of 48px.
31
+
32
+ Three traces to find, because every instrument pointed at the scroll logic
33
+ and the scroll logic was innocent. The first two also measured the wrong
34
+ element — during an answer the last editable belongs to the turn being
35
+ written, and afterwards to a different turn entirely, so the trace read a
36
+ change of subject as a movement.
37
+
38
+ ## 0.54.1 — 2026-09-12
39
+
40
+ ### Fixed
41
+
42
+ - **Scrolling up was fought for the first 64 pixels, then let go all at
43
+ once.** Since 0.54.0 measured the room under the last turn, the view comes
44
+ to rest exactly at the end of the scroll — so every upward scroll now starts
45
+ inside the band that counts as "back at the end", and the scroll event
46
+ re-acquired a reader who was still on their way out. Measured at rest, with
47
+ a real wheel:
48
+
49
+ | wheeled up | wanted | ended at |
50
+ | --- | --- | --- |
51
+ | 20px | 134 | **154** |
52
+ | 40px | 114 | **154** |
53
+ | 63px | 91 | **154** |
54
+ | 80px | 74 | 74 |
55
+
56
+ Arriving and leaving are different questions and no longer share a number.
57
+ `threshold` stays what it was — how far you must go before the view accepts
58
+ you have left, generous so a nudge does not abandon the answer you are
59
+ reading. Coming back now means coming back: four pixels, a rounding
60
+ allowance rather than an opinion.
61
+
62
+ A test asserted the old behaviour in so many words — "inside the 64px
63
+ threshold" — which is the fault written down as a rule. It says the opposite
64
+ now, beside one that pins the fault.
65
+
66
+ ### Added
67
+
68
+ - **[Getting started](./getting-started.md)** — one file, ten minutes, a chat
69
+ that runs with no backend, then one paragraph to point it at a model. The
70
+ README's first example was a single `<ChatInput>`, which is a text box: to
71
+ get the experience the page describes you also needed `useChatTurns`,
72
+ `Conversation` and the anchoring, and those were three sections apart with no
73
+ runnable file between them.
74
+
75
+ The example is `src/examples/minimal.tsx`, so `tsc -b` compiles it on every
76
+ build; a test asserts the page quotes it character for character; and a
77
+ Storybook story renders it, so the claim that it works is checked three ways
78
+ and none of them is a screenshot somebody took once. Nothing imports it, so
79
+ the library build never reaches it — verified at zero occurrences in the
80
+ bundle.
81
+
82
+ ### Fixed
83
+
84
+ - **The default placeholder was `"Placeholder text..."`** — a stand-in nobody
85
+ replaced. A default is what every consumer who has not thought about it yet
86
+ ships in production, which is most of them on their first afternoon. It is
87
+ `"Ask anything…"` now. Found by writing the getting-started page and looking
88
+ at what it drew, which is the argument for having one.
89
+
90
+ - **The changelog promised two versions that were never published.** The
91
+ registry goes `0.52.0` → `0.54.0`; this file described `0.52.1` and `0.53.0`
92
+ with dates, so anyone reaching for `npm i inline-chat-kit@0.53.0` got
93
+ nothing. Both are marked, and their changes shipped in `0.54.0`.
94
+
9
95
  ## 0.54.0 — 2026-09-12
10
96
 
11
97
  Everything here came out of using the kit on a phone. It is better there than
@@ -202,6 +288,10 @@ see *Where it is meant to run*.
202
288
 
203
289
  ## 0.53.0 — 2026-09-03
204
290
 
291
+ > **Never published.** This version exists in this file and not on npm —
292
+ > the registry goes `0.52.0` → `0.54.0`. Everything below shipped in 0.54.0,
293
+ > which is the version to install if you came here looking for it.
294
+
205
295
  ### Changed
206
296
 
207
297
  - **The artifact card moves instead of changing colour.** Pointing at it lifted
@@ -272,6 +362,10 @@ see *Where it is meant to run*.
272
362
 
273
363
  ## 0.52.1 — 2026-09-03
274
364
 
365
+ > **Never published.** This version exists in this file and not on npm —
366
+ > the registry goes `0.52.0` → `0.54.0`. Everything below shipped in 0.54.0,
367
+ > which is the version to install if you came here looking for it.
368
+
275
369
  ### Fixed
276
370
 
277
371
  - **The composer's editor was 16px wider than the box that clips it.**
package/README.md CHANGED
@@ -10,6 +10,10 @@ answers you can draw on with a marker,
10
10
  syntax-highlighted code blocks, a scroll container that keeps up with an
11
11
  answer, and reply-in-thread popups.
12
12
 
13
+ **New here?** [**Getting started**](./getting-started.md) is one file, ten
14
+ minutes, and a chat that runs with no backend. The rest of this page is the
15
+ reference.
16
+
13
17
  ## Where it is meant to run
14
18
 
15
19
  **Desktop and tablet.** That is what this is designed for, tuned for, and what
@@ -0,0 +1 @@
1
+ export declare function MinimalChat(): import("react").JSX.Element;
@@ -747,7 +747,7 @@ var Ue = {
747
747
  n.deleteContents();
748
748
  let r = document.createTextNode(e);
749
749
  return n.insertNode(r), n.setStartAfter(r), n.collapse(!0), t.removeAllRanges(), t.addRange(n), !1;
750
- }, Ke = n(function({ state: e, value: t, onChange: n, onSubmit: r, onStop: i, onAdd: l, attachments: u, onAttach: p, onRemoveAttachment: C, accept: w = "image/*", multiple: T = !1, onTranscribe: E, onCopy: D, onEdit: O, onCancelEdit: k, isEditing: A = !1, placeholder: j = "Placeholder text...", animationConfig: M, style: N }, P) {
750
+ }, Ke = n(function({ state: e, value: t, onChange: n, onSubmit: r, onStop: i, onAdd: l, attachments: u, onAttach: p, onRemoveAttachment: C, accept: w = "image/*", multiple: T = !1, onTranscribe: E, onCopy: D, onEdit: O, onCancelEdit: k, isEditing: A = !1, placeholder: j = "Ask anything…", animationConfig: M, style: N }, P) {
751
751
  let ee = s(), F = d(null), I = d(null), te = d(null), ne = d(0), re = d(!1), ie = d(null), [ae, oe] = f(!1), [se, ce] = f(!1), [le, ue] = f(!1), [fe, pe] = f(!0), [me, he] = f(!1), [L, ge] = f(!1), [_e, z] = f(!1), ve = d(0), B = d(!1), H = d(0), ye = d(!1), U = d(null);
752
752
  o(() => () => {
753
753
  U.current && clearTimeout(U.current);
@@ -853,7 +853,7 @@ var Ue = {
853
853
  Le,
854
854
  et
855
855
  ]), o(() => {
856
- (e === "idle" || e === "typing") && F.current?.focus();
856
+ (e === "idle" || e === "typing") && F.current?.focus({ preventScroll: !0 });
857
857
  }, [e]), o(() => {
858
858
  let e = F.current;
859
859
  e && (ve.current = e.scrollHeight || 17);
@@ -4938,25 +4938,25 @@ var Xr = {
4938
4938
  viewport: "ick-viewport-C3hz",
4939
4939
  content: "ick-content-33lN",
4940
4940
  jump: "ick-jump-nd6O"
4941
- }, Zr = 64, Qr = 500, $r = (e, t) => e.offsetTop - t.offsetTop, ei = (e, t) => t ? e.querySelector(`[id="${CSS.escape(t)}"]`) : null, ti = (e, t, n) => {
4941
+ }, Zr = 64, Qr = 500, $r = 4, ei = (e, t) => e.offsetTop - t.offsetTop, ti = (e, t) => t ? e.querySelector(`[id="${CSS.escape(t)}"]`) : null, ni = (e, t, n) => {
4942
4942
  n !== t.current && (t.current = n, e.style.setProperty("--ick-conversation-tail", `${Math.round(n)}px`));
4943
- }, ni = n(function({ children: e, anchorId: t, anchorOffset: n = 0, endOffset: r = 0, tail: i = "auto", threshold: s = Zr, scrollButton: u = !0, scrollButtonLabel: p = "Jump to the latest", follow: m = !0, className: _, viewportClassName: v, onScroll: y, ...b }, x) {
4943
+ }, ri = n(function({ children: e, anchorId: t, anchorOffset: n = 0, endOffset: r = 0, tail: i = "auto", threshold: s = Zr, scrollButton: u = !0, scrollButtonLabel: p = "Jump to the latest", follow: m = !0, className: _, viewportClassName: v, onScroll: y, ...b }, x) {
4944
4944
  let S = d(null);
4945
4945
  c(x, () => S.current, []);
4946
4946
  let C = d(null), [w, E] = f(!0), D = d(-1), O = d(null), k = d(!1), A = a(() => {
4947
4947
  let e = S.current, t = C.current;
4948
4948
  if (!e || !t) return 0;
4949
4949
  if (i === "auto" && k.current) return Math.max(0, e.scrollHeight - e.clientHeight);
4950
- let n = t.lastElementChild, a = n ? $r(n, e) + n.offsetHeight : $r(t, e) + t.offsetHeight - D.current;
4950
+ let n = t.lastElementChild, a = n ? ei(n, e) + n.offsetHeight : ei(t, e) + t.offsetHeight - D.current;
4951
4951
  return Math.max(0, a + r - e.clientHeight);
4952
4952
  }, [r, i]), j = a(() => {
4953
4953
  let e = S.current, t = C.current;
4954
4954
  if (!e || !t) return;
4955
- if (i !== "auto") return ti(t, D, Math.max(0, i));
4956
- let a = parseFloat(getComputedStyle(e).paddingBottom) || 0, o = Math.max(0, r - a), s = t.lastElementChild, c = ei(e, O.current);
4957
- if (k.current = !!s && !!c, !k.current || !s || !c) return ti(t, D, o);
4958
- let l = $r(s, e) + s.offsetHeight - $r(c, e);
4959
- ti(t, D, Math.max(o, e.clientHeight - n - l - a));
4955
+ if (i !== "auto") return ni(t, D, Math.max(0, i));
4956
+ let a = parseFloat(getComputedStyle(e).paddingBottom) || 0, o = Math.max(0, r - a), s = t.lastElementChild, c = ti(e, O.current);
4957
+ if (k.current = !!s && !!c, !k.current || !s || !c) return ni(t, D, o);
4958
+ let l = ei(s, e) + s.offsetHeight - ei(c, e);
4959
+ ni(t, D, Math.max(o, e.clientHeight - n - l - a));
4960
4960
  }, [
4961
4961
  i,
4962
4962
  n,
@@ -4967,7 +4967,7 @@ var Xr = {
4967
4967
  if (t) {
4968
4968
  let r = e.querySelector(`[id="${CSS.escape(t)}"]`);
4969
4969
  if (r) {
4970
- let t = Math.max(0, e.scrollHeight - e.clientHeight), i = $r(r, e), a = i - n, o = i + r.offsetHeight - e.clientHeight;
4970
+ let t = Math.max(0, e.scrollHeight - e.clientHeight), i = ei(r, e), a = i - n, o = i + r.offsetHeight - e.clientHeight;
4971
4971
  return Math.max(0, Math.min(Math.max(a, o), t));
4972
4972
  }
4973
4973
  }
@@ -4996,12 +4996,20 @@ var Xr = {
4996
4996
  let e = S.current, n = C.current;
4997
4997
  if (!e || !n || typeof ResizeObserver > "u") return;
4998
4998
  let r = () => {
4999
- let t = M();
4999
+ let n = M();
5000
5000
  if (N.current) {
5001
- if (performance.now() < N.current && Math.abs(e.scrollTop - t) > 1) return;
5001
+ if (performance.now() < N.current && Math.abs(e.scrollTop - n) > 1) return;
5002
5002
  N.current = 0;
5003
5003
  }
5004
- e.scrollTop = t;
5004
+ let r = n - e.scrollTop;
5005
+ if (O.current && !t && Math.abs(r) > s && !K()) {
5006
+ N.current = performance.now() + Qr, e.scrollTo({
5007
+ top: n,
5008
+ behavior: "smooth"
5009
+ });
5010
+ return;
5011
+ }
5012
+ e.scrollTop = n;
5005
5013
  }, i = t != null && t !== P.current;
5006
5014
  t && (P.current = t), i && !K() && Math.abs(e.scrollTop - M()) > s ? (N.current = performance.now() + Qr, e.scrollTo({
5007
5015
  top: M(),
@@ -5052,11 +5060,10 @@ var Xr = {
5052
5060
  let F = a((e) => {
5053
5061
  if (y?.(e), !m) return;
5054
5062
  let t = S.current;
5055
- t && Math.abs(t.scrollTop - M()) <= s && (N.current = 0, E(!0));
5063
+ t && Math.abs(t.scrollTop - M()) <= $r && (N.current = 0, E(!0));
5056
5064
  }, [
5057
5065
  m,
5058
5066
  y,
5059
- s,
5060
5067
  M
5061
5068
  ]), I = m && !w;
5062
5069
  return /* @__PURE__ */ g("div", {
@@ -5085,14 +5092,14 @@ var Xr = {
5085
5092
  })
5086
5093
  })]
5087
5094
  });
5088
- }), ri = 24, ii = 480, ai = 80, oi = 260;
5089
- function si(e, t, n = Infinity) {
5090
- let r = Math.max(t - 48, 0), i = Math.min(Math.max(e.width + ai, ii), r), a = e.left + e.width / 2 - i / 2, o = Math.min(Math.max(a, ri), t - ri - i), s = Math.max(ri, n - ri - oi), c = Math.min(Math.max(e.top - ri, Math.min(ri, n)), Number.isFinite(s) ? s : Infinity);
5095
+ }), ii = 24, ai = 480, oi = 80, si = 260;
5096
+ function ci(e, t, n = Infinity) {
5097
+ let r = Math.max(t - 48, 0), i = Math.min(Math.max(e.width + oi, ai), r), a = e.left + e.width / 2 - i / 2, o = Math.min(Math.max(a, ii), t - ii - i), s = Math.max(ii, n - ii - si), c = Math.min(Math.max(e.top - ii, Math.min(ii, n)), Number.isFinite(s) ? s : Infinity);
5091
5098
  return {
5092
5099
  x: o,
5093
5100
  y: c,
5094
5101
  width: i,
5095
- maxHeight: Number.isFinite(n) ? Math.max(oi, n - c - ri) : Infinity
5102
+ maxHeight: Number.isFinite(n) ? Math.max(si, n - c - ii) : Infinity
5096
5103
  };
5097
5104
  }
5098
5105
  var $ = {
@@ -5116,7 +5123,7 @@ var $ = {
5116
5123
  turn: "ick-turn-ogjU",
5117
5124
  turnRow: "ick-turnRow-0OuP",
5118
5125
  srOnly: "ick-srOnly-LwE1"
5119
- }, ci = 760, li = {
5126
+ }, li = 760, ui = {
5120
5127
  closed: {
5121
5128
  y: "100%",
5122
5129
  opacity: 1,
@@ -5135,11 +5142,11 @@ var $ = {
5135
5142
  bounce: .18
5136
5143
  }
5137
5144
  }
5138
- }, ui = "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [contenteditable]:not([contenteditable=\"false\"]), [tabindex]:not([tabindex=\"-1\"])";
5139
- function di({ activeReply: e, onClose: t, onSave: n, onSendMessage: r }) {
5140
- let [i, a] = f("none"), c = d(null), l = d(null), u = d(null), [p] = f(() => typeof document > "u" ? null : document.activeElement), m = s(), _ = s(), v = Ue, [b, S] = f(() => typeof window > "u" ? !1 : window.matchMedia?.(`(max-width: ${ci}px)`)?.matches ?? !1);
5145
+ }, di = "a[href], button:not([disabled]), input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [contenteditable]:not([contenteditable=\"false\"]), [tabindex]:not([tabindex=\"-1\"])";
5146
+ function fi({ activeReply: e, onClose: t, onSave: n, onSendMessage: r }) {
5147
+ let [i, a] = f("none"), c = d(null), l = d(null), u = d(null), [p] = f(() => typeof document > "u" ? null : document.activeElement), m = s(), _ = s(), v = Ue, [b, S] = f(() => typeof window > "u" ? !1 : window.matchMedia?.(`(max-width: ${li}px)`)?.matches ?? !1);
5141
5148
  o(() => {
5142
- let e = window.matchMedia?.(`(max-width: ${ci}px)`);
5149
+ let e = window.matchMedia?.(`(max-width: ${li}px)`);
5143
5150
  if (!e?.addEventListener) return;
5144
5151
  let t = () => S(e.matches);
5145
5152
  return t(), e.addEventListener("change", t), () => e.removeEventListener("change", t);
@@ -5155,7 +5162,7 @@ function di({ activeReply: e, onClose: t, onSave: n, onSendMessage: r }) {
5155
5162
  return;
5156
5163
  }
5157
5164
  if (e.key !== "Tab") return;
5158
- let n = [...u.current?.querySelectorAll(ui) ?? []];
5165
+ let n = [...u.current?.querySelectorAll(di) ?? []];
5159
5166
  if (n.length === 0) return;
5160
5167
  let r = n[0], i = n[n.length - 1], a = document.activeElement;
5161
5168
  e.shiftKey && a === r ? (e.preventDefault(), i.focus()) : !e.shiftKey && a === i && (e.preventDefault(), r.focus());
@@ -5192,7 +5199,7 @@ function di({ activeReply: e, onClose: t, onSave: n, onSendMessage: r }) {
5192
5199
  y: 0,
5193
5200
  width: 480,
5194
5201
  maxHeight: Infinity
5195
- } : si(e.rect, window.innerWidth, window.innerHeight);
5202
+ } : ci(e.rect, window.innerWidth, window.innerHeight);
5196
5203
  return /* @__PURE__ */ h(y, {
5197
5204
  reducedMotion: "user",
5198
5205
  children: /* @__PURE__ */ h("div", {
@@ -5226,7 +5233,7 @@ function di({ activeReply: e, onClose: t, onSave: n, onSendMessage: r }) {
5226
5233
  scale: .95,
5227
5234
  filter: "blur(4px)"
5228
5235
  },
5229
- variants: b ? li : void 0,
5236
+ variants: b ? ui : void 0,
5230
5237
  transition: b ? void 0 : {
5231
5238
  type: "spring",
5232
5239
  bounce: 0,
@@ -5370,7 +5377,7 @@ function di({ activeReply: e, onClose: t, onSave: n, onSendMessage: r }) {
5370
5377
  }
5371
5378
  //#endregion
5372
5379
  //#region src/CustomCursor/CustomCursor.tsx
5373
- function fi() {
5380
+ function pi() {
5374
5381
  let e = C(-100), t = C(-100), [n, r] = f("default"), [i, a] = f(!1), [s, c] = f(!1);
5375
5382
  return o(() => {
5376
5383
  let n = (n) => {
@@ -5528,20 +5535,20 @@ function fi() {
5528
5535
  }
5529
5536
  //#endregion
5530
5537
  //#region src/GlassButton/GlassButton.tsx
5531
- var pi = {
5538
+ var mi = {
5532
5539
  s: "m",
5533
5540
  m: "l",
5534
5541
  l: "xl"
5535
- }, mi = n(function({ size: e = "m", iconLeft: t, ...n }, r) {
5542
+ }, hi = n(function({ size: e = "m", iconLeft: t, ...n }, r) {
5536
5543
  return /* @__PURE__ */ h(V, {
5537
5544
  ref: r,
5538
5545
  variant: "glass",
5539
- size: pi[e],
5546
+ size: mi[e],
5540
5547
  icon: t,
5541
5548
  ...n
5542
5549
  });
5543
5550
  });
5544
5551
  //#endregion
5545
- export { Oe as AddCardsOverlay, ht as AnswerActions, tn as Approval, Rn as ArtifactCard, Ir as ArtifactPane, Ce as Attachments, vt as Branch, V as Button, cn as ChainOfThought, lt as ChatHeader, Ke as ChatInput, Kr as ChatLayout, Or as ChatTurnRow, pn as Chip, Wt as CodeBlock, Pr as Context, ni as Conversation, fi as CustomCursor, Yr as EmptyState, kn as FOLDABLE_FROM, mi as GlassButton, Ee as HoverActionsRow, hr as InlineCitation, bt as Loader, Te as MorphGlyph, mn as QuestionBadge, Dn as QuestionCard, gn as QuestionFieldRow, Mn as QuestionGroup, hn as QuestionOptionRow, _n as QuestionOtherRow, vn as QuestionShell, At as Reasoning, di as ReplyThreadPopup, dn as Sources, Pn as SystemMessage, an as TaskList, Tr as TextHighlighter, Zt as Tool, G as announce, Tn as answerChips, It as canHighlight, An as defaultFoldMotion, Ue as defaultInlineAnimConfig, U as formatSize, rt as headerIconSize, Vt as loadHighlighter, qe as mergeParts, qr as useArtifacts, tt as useChatTurns, ze as useVoiceInput };
5552
+ export { Oe as AddCardsOverlay, ht as AnswerActions, tn as Approval, Rn as ArtifactCard, Ir as ArtifactPane, Ce as Attachments, vt as Branch, V as Button, cn as ChainOfThought, lt as ChatHeader, Ke as ChatInput, Kr as ChatLayout, Or as ChatTurnRow, pn as Chip, Wt as CodeBlock, Pr as Context, ri as Conversation, pi as CustomCursor, Yr as EmptyState, kn as FOLDABLE_FROM, hi as GlassButton, Ee as HoverActionsRow, hr as InlineCitation, bt as Loader, Te as MorphGlyph, mn as QuestionBadge, Dn as QuestionCard, gn as QuestionFieldRow, Mn as QuestionGroup, hn as QuestionOptionRow, _n as QuestionOtherRow, vn as QuestionShell, At as Reasoning, fi as ReplyThreadPopup, dn as Sources, Pn as SystemMessage, an as TaskList, Tr as TextHighlighter, Zt as Tool, G as announce, Tn as answerChips, It as canHighlight, An as defaultFoldMotion, Ue as defaultInlineAnimConfig, U as formatSize, rt as headerIconSize, Vt as loadHighlighter, qe as mergeParts, qr as useArtifacts, tt as useChatTurns, ze as useVoiceInput };
5546
5553
 
5547
5554
  //# sourceMappingURL=inline-chat-kit.js.map