inline-chat-kit 0.54.2 → 0.55.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 +102 -0
- package/dist/ChatExperience/ChatExperience.d.ts +115 -0
- package/dist/ChatExperience-CRtVStGQ.js +5839 -0
- package/dist/ChatExperience-CRtVStGQ.js.map +1 -0
- package/dist/demo/ChatExperienceDemo.d.ts +27 -0
- package/dist/demo/InlineChatBanner.d.ts +6 -0
- package/dist/demo/IntroLanding.d.ts +43 -0
- package/dist/demo/featureStatus.d.ts +18 -0
- package/dist/demo/index.d.ts +8 -0
- package/dist/demo/scriptedApi.d.ts +49 -0
- package/dist/demo.js +691 -0
- package/dist/demo.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/inline-chat-kit.css +1 -1
- package/dist/inline-chat-kit.js +8 -5540
- package/dist/inline-chat-kit.js.map +1 -1
- package/getting-started.md +33 -69
- package/package.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,108 @@ 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.55.0 — 2026-09-13
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **`ChatExperience` — the whole thing, assembled.** Every other export here is
|
|
16
|
+
a piece: a header, a scroll container, a turn, a pane. Putting them together
|
|
17
|
+
was a page's job right up until two pages did it, and then it was a copy.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
<ChatExperience onSend={ask} title="Chat" placeholder="Ask anything…" />
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
It holds a sent turn at the top while its answer is written and lets go when
|
|
24
|
+
it settles; titles the header from the first question actually asked rather
|
|
25
|
+
than from the first keystroke; places the artifact pane *beside* the
|
|
26
|
+
conversation rather than inside it, which is the difference between a pane
|
|
27
|
+
and a modal; announces a full context window once, through the kit's own live
|
|
28
|
+
region rather than a second one; and asks whether there is a pointer before
|
|
29
|
+
drawing a cursor for it. A host gives it content and identity — `onSend`, the
|
|
30
|
+
copy, what is in the pane, where "back" goes.
|
|
31
|
+
|
|
32
|
+
The pieces are all still exported. Assemble them yourself when your app needs
|
|
33
|
+
a shape this one does not have.
|
|
34
|
+
|
|
35
|
+
- **`inline-chat-kit/demo`** — the scripted showcase behind its own entry
|
|
36
|
+
point: the landing page, the banner, and the particle-physics answers. An app
|
|
37
|
+
that never imports it never carries a line of it; the library entry is 1.8 kB
|
|
38
|
+
and the demo is 22.
|
|
39
|
+
|
|
40
|
+
### Fixed
|
|
41
|
+
|
|
42
|
+
- **Two variables the kit expected a host to define.** The chat stylesheet said
|
|
43
|
+
`var(--bg)` and the landing page said `--color-bg-page`, `--font-geist-sans`
|
|
44
|
+
and `--font-jetbrains-mono` — none of which this package ships. They rendered
|
|
45
|
+
correctly in a host that happened to have them and silently wrong in one that
|
|
46
|
+
did not: a header see-through over a scrolling answer, and an intro in serif
|
|
47
|
+
on a white page. All of it is on `--ick-` tokens now.
|
|
48
|
+
|
|
49
|
+
### Internal
|
|
50
|
+
|
|
51
|
+
- **The fork that caused the last three bug reports is gone.** Two apps each
|
|
52
|
+
kept their own copy of the assembly — 865 lines and 890, ~90% identical —
|
|
53
|
+
along with 537 byte-identical lines of scripted answers and two stylesheets.
|
|
54
|
+
Every fix went into one of them.
|
|
55
|
+
|
|
56
|
+
- **`noForks.test.ts`** measures it from now on: any file under `apps/` sharing
|
|
57
|
+
more than half its substantial lines with a file in the kit fails, naming
|
|
58
|
+
both files and the share. Comments are stripped so a quotation is not a fork,
|
|
59
|
+
and whitespace is normalised so a reformat is not a defence. Watched failing
|
|
60
|
+
before it was trusted — a planted copy reports `100% (327 of 327 lines)`.
|
|
61
|
+
|
|
62
|
+
- The getting-started example is now the one-liner, compiled on every build and
|
|
63
|
+
quoted into the page character for character, as before.
|
|
64
|
+
|
|
65
|
+
## 0.54.3 — 2026-09-13
|
|
66
|
+
|
|
67
|
+
### Fixed
|
|
68
|
+
|
|
69
|
+
- **The sent message flickered a pixel up and down for the whole length of
|
|
70
|
+
every answer after the first.** Measured while a second answer was written:
|
|
71
|
+
the message sat at y=100 and flipped to 101 and back thirty-one times, with
|
|
72
|
+
the room under the conversation going 418, 379, 374, 372, 362, 360, 351
|
|
73
|
+
underneath it — a new value on every frame.
|
|
74
|
+
|
|
75
|
+
It was a feedback loop. That room is part of `scrollHeight`, the scroll
|
|
76
|
+
target is clamped to `scrollHeight`, and the room was recomputed from
|
|
77
|
+
measurements on every frame: the room moved the view, the view moved the
|
|
78
|
+
measurements, and the measurements moved the room. Sized to be *exactly*
|
|
79
|
+
enough for the anchored turn to reach the anchor, the clamp sat on a knife
|
|
80
|
+
edge and a fraction of a pixel chose the side.
|
|
81
|
+
|
|
82
|
+
A floor does not drop while the thing standing on it is still standing. So
|
|
83
|
+
while a turn is anchored the room only grows, and it is recomputed the
|
|
84
|
+
moment the anchor is let go — a long answer carries some spare room until it
|
|
85
|
+
settles, and nothing carries it afterwards. Growing stays allowed, which is
|
|
86
|
+
what keeps it self-correcting: a first measurement taken before layout is
|
|
87
|
+
final is raised by the next one rather than standing for the whole answer.
|
|
88
|
+
|
|
89
|
+
After: the message decelerates into the anchor and then holds one position
|
|
90
|
+
for the rest of the answer — 473 frames at a 680px viewport, 466 at 1400px,
|
|
91
|
+
not one of them a pixel off.
|
|
92
|
+
|
|
93
|
+
### Internal
|
|
94
|
+
|
|
95
|
+
- **`follow-check` has a guard for it**, and it had none before: every check
|
|
96
|
+
in there measured a *transition* — where a message lands, where the view
|
|
97
|
+
comes to rest, whether it travelled — and this fault lived in the quiet
|
|
98
|
+
stretch between them, which is why it was reported three times and "fixed"
|
|
99
|
+
twice on measurements that were each true.
|
|
100
|
+
|
|
101
|
+
The window comes from `aria-busy`, which the turn already carries for
|
|
102
|
+
screen readers while its answer arrives, so it is the window the assertion
|
|
103
|
+
names rather than one inferred from the turn's height — that one ran past
|
|
104
|
+
the end of the answer and counted the settle. The fault inside it is a
|
|
105
|
+
**reversal**, because jitter is not motion, it is motion that changes its
|
|
106
|
+
mind; the arrival is required to move and is asserted elsewhere. A creep
|
|
107
|
+
never reverses, so the back half of the window has to be a single number as
|
|
108
|
+
well, and that number the anchor.
|
|
109
|
+
|
|
110
|
+
Watched failing on the fault before it was trusted: 4 reversals at both
|
|
111
|
+
heights with the room recomputing per frame, none with the floor.
|
|
112
|
+
|
|
11
113
|
## 0.54.2 — 2026-09-12
|
|
12
114
|
|
|
13
115
|
### Fixed
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { ChatHeaderAction } from '../ChatHeader/ChatHeader';
|
|
3
|
+
import { ReplyThreadPopupProps } from '../ReplyThreadPopup/ReplyThreadPopup';
|
|
4
|
+
import { UseChatTurnsOptions } from '../useChatTurns/useChatTurns';
|
|
5
|
+
import { FoldMotion } from '../QuestionGroup/QuestionGroup';
|
|
6
|
+
import { InlineAnimConfig } from '../ChatInput/ChatInput';
|
|
7
|
+
import { TranscribeHandler } from '../voice/useVoiceInput';
|
|
8
|
+
import { TurnPartUpdate } from '../turnParts/turnParts';
|
|
9
|
+
import { Answer } from '../QuestionCard/types';
|
|
10
|
+
import { Decision } from '../Approval/Approval';
|
|
11
|
+
/**
|
|
12
|
+
* The whole thing, assembled.
|
|
13
|
+
*
|
|
14
|
+
* ## Why this is in the kit and not in your page
|
|
15
|
+
*
|
|
16
|
+
* Every other export here is a piece: a header, a scroll container, a turn, a
|
|
17
|
+
* pane. Putting them together is a page's job right up until two pages do it,
|
|
18
|
+
* and then it is a copy — which is exactly what happened. The playground's
|
|
19
|
+
* page and the website's page were 865 and 890 lines of ~90% identical code,
|
|
20
|
+
* along with 537 identical lines of scripted answers and 300 of stylesheet.
|
|
21
|
+
*
|
|
22
|
+
* That copy is where the last three reported faults lived. Not in the kit: in
|
|
23
|
+
* the fact that a fix went into one page and the other kept the old one. The
|
|
24
|
+
* header rendered see-through because the second copy never defined `--bg`.
|
|
25
|
+
* The empty state snapped out of existence because the second copy never got
|
|
26
|
+
* the `AnimatePresence`. The sent message stayed pinned to the top for the
|
|
27
|
+
* rest of the session because the second copy never got the released anchor.
|
|
28
|
+
*
|
|
29
|
+
* So the assembly lives here, where a fix is made once and arrives everywhere
|
|
30
|
+
* by `npm install`. What stays a host's: the answers, the copy, the artifact's
|
|
31
|
+
* contents, the logo, where "back" goes. Content and identity — the two things
|
|
32
|
+
* a library cannot know and should not guess.
|
|
33
|
+
*
|
|
34
|
+
* ## What it does that a page would have to remember to
|
|
35
|
+
*
|
|
36
|
+
* Holds the sent turn at the top while its answer is written and lets go when
|
|
37
|
+
* it settles. Keeps a title from the first question actually asked rather than
|
|
38
|
+
* from the first keystroke. Announces a full context window once, through the
|
|
39
|
+
* kit's own live region rather than a second one. Places the artifact pane
|
|
40
|
+
* *beside* the conversation rather than inside it, which is the difference
|
|
41
|
+
* between a pane and a modal. Asks whether there is a pointer before drawing a
|
|
42
|
+
* cursor for it.
|
|
43
|
+
*/
|
|
44
|
+
/** The opening block, before anybody has asked anything. */
|
|
45
|
+
export interface ChatExperienceEmpty {
|
|
46
|
+
title: string;
|
|
47
|
+
description?: string;
|
|
48
|
+
/** One opener per branch of your `onSend`, ideally: everything the kit can
|
|
49
|
+
draw becomes reachable by pressing something rather than by knowing what
|
|
50
|
+
to type. Sent, not typed into the box — an opener that only fills the
|
|
51
|
+
input asks somebody to press send on a sentence they did not write. */
|
|
52
|
+
suggestions?: string[];
|
|
53
|
+
}
|
|
54
|
+
/** What the pane shows while an artifact is open. The kit draws the pane; what
|
|
55
|
+
is inside it is the host's, which is why this returns children. */
|
|
56
|
+
export interface ChatExperienceArtifact {
|
|
57
|
+
title: string;
|
|
58
|
+
meta?: string;
|
|
59
|
+
children: ReactNode;
|
|
60
|
+
}
|
|
61
|
+
/** A part the host drives, handed the writer for the turn it belongs to. */
|
|
62
|
+
export type PartWriter = (turnId: string, part: TurnPartUpdate) => void;
|
|
63
|
+
export interface ChatExperienceProps {
|
|
64
|
+
/** Where answers come from. Return a string, a promise of one, or an async
|
|
65
|
+
iterable of deltas — see `useChatTurns`. */
|
|
66
|
+
onSend: UseChatTurnsOptions["onSend"];
|
|
67
|
+
/** Speech to text. The kit records; the host transcribes. Omit and the
|
|
68
|
+
microphone does not appear. */
|
|
69
|
+
onTranscribe?: TranscribeHandler;
|
|
70
|
+
/** A follow-up asked on a passage. Omit and replying in a thread is off. */
|
|
71
|
+
onThreadReply?: ReplyThreadPopupProps["onSendMessage"];
|
|
72
|
+
/** Shown in the header until a question has actually been asked. */
|
|
73
|
+
title?: string;
|
|
74
|
+
/** Where the back control goes. */
|
|
75
|
+
backHref?: string;
|
|
76
|
+
backLabel?: string;
|
|
77
|
+
/** Extra header actions, added after the ones this manages. */
|
|
78
|
+
actions?: ChatHeaderAction[];
|
|
79
|
+
placeholder?: string;
|
|
80
|
+
empty?: ChatExperienceEmpty;
|
|
81
|
+
/** The context meter. Leave `contextTotal` off and there is no meter. A real
|
|
82
|
+
app reads this off its API's usage; a demo can count it off the text. */
|
|
83
|
+
contextTotal?: number;
|
|
84
|
+
/** What a system prompt and the tool definitions cost before anybody types. */
|
|
85
|
+
contextBase?: number;
|
|
86
|
+
/** The pane beside the conversation, asked for the artifact that is open. */
|
|
87
|
+
artifact?: (openId: string) => ChatExperienceArtifact | null;
|
|
88
|
+
/** The theme, if the host keeps it. Left off, this manages its own and puts
|
|
89
|
+
a toggle in the header; `data-theme` on the root element either way, and
|
|
90
|
+
unset until somebody chooses, so the kit follows the system preference —
|
|
91
|
+
which is what it is there for. */
|
|
92
|
+
theme?: "light" | "dark" | null;
|
|
93
|
+
onThemeChange?: (theme: "light" | "dark") => void;
|
|
94
|
+
/** The pointer-following cursor, and the rule that hides the real one. Only
|
|
95
|
+
ever where there is a pointer to replace. */
|
|
96
|
+
cursor?: boolean;
|
|
97
|
+
/** The freeform-marker / precise-selection pair in the header. */
|
|
98
|
+
selectionToggle?: boolean;
|
|
99
|
+
/** How far below the top edge a sent message comes to rest. Sets the
|
|
100
|
+
conversation's own top padding too — the two have to agree, so one number
|
|
101
|
+
writes both. */
|
|
102
|
+
anchorOffset?: number;
|
|
103
|
+
/** Room left under the composer once an answer settles. */
|
|
104
|
+
endOffset?: number;
|
|
105
|
+
/** Motion, opened up so a tuning panel can reach it. */
|
|
106
|
+
animationConfig?: InlineAnimConfig;
|
|
107
|
+
foldMotion?: FoldMotion;
|
|
108
|
+
/** How long the feed waits before its first row arrives. */
|
|
109
|
+
feedDelay?: number;
|
|
110
|
+
onAnswerQuestion?: (write: PartWriter, turnId: string, partId: string, questionId: string, answer: Answer) => void;
|
|
111
|
+
onEditQuestion?: (write: PartWriter, turnId: string, partId: string, index: number) => void;
|
|
112
|
+
onDecideApproval?: (write: PartWriter, turnId: string, partId: string, decision: Decision) => void;
|
|
113
|
+
className?: string;
|
|
114
|
+
}
|
|
115
|
+
export declare function ChatExperience({ onSend, onTranscribe, onThreadReply, title, 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;
|