inline-chat-kit 0.60.0 → 0.61.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/CHANGELOG.md CHANGED
@@ -8,6 +8,84 @@ 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.61.1 — 2026-10-15
12
+
13
+ ### Fixed
14
+
15
+ The opening of a conversation, measured in the playground at 1280×800.
16
+
17
+ - **The openers stay while somebody types.** They went at the first letter —
18
+ title, description and seven openers in one frame — and inline the
19
+ composer jumped with them, from the middle of the page to its top-right
20
+ corner, 560px wide to 320. They stay now until a message is sent, and the
21
+ composer stays in their column.
22
+ - **Then they step out of the way.** On a send — typed, or an opener pressed
23
+ — the openers leave to the left, blurred, one after another: 160ms each,
24
+ 25ms apart, ease-out. They leave the layout at once, so the message goes
25
+ straight to where it belongs instead of waiting for them.
26
+ - **Pinning the composer to the bottom moves the openers with it.** They
27
+ jumped 93px in a frame while the composer travelled; they ride its spring
28
+ now. And the composer arrives without going past: it overshot the bottom
29
+ edge by 12px and came back. A box that is still a box moves on its spring
30
+ critically damped; a sent message keeps the spring it had.
31
+ - **Inline too, an answer starts once its question has landed.** The first
32
+ one was drawn under openers that were still leaving. 0.60.0 did this
33
+ docked only.
34
+
35
+
36
+ A host's own button, which 0.60.0 turned down. The reason it was turned down
37
+ still holds for one button, and only that one is kept back. Nothing changes for
38
+ a host that passes nothing: every story screenshotted against 0.60.0 in both
39
+ themes, 138 stories, identical but for the one that sets its own
40
+ `--ick-btn-radius` — its composer is concentric now, which is the point — and,
41
+ on a touch screen, the composer's "+" and microphone, below.
42
+
43
+ ### Added
44
+
45
+ - **`components={{ Button }}` on `ChatExperience`**, and `ComponentsProvider`
46
+ for pieces assembled by hand, in the way of `LabelsProvider`. The host's
47
+ button is drawn for every role in theming.md's table but one: the
48
+ composer's send / stop / save, whose glyph morphs between the three inside
49
+ the button. That one stays the kit's and is themed by the tokens, as before.
50
+ - The kit keeps the layout and the behaviour, the host's button paints. It
51
+ is drawn inside a box the kit sizes from `--ick-control-*` (square when
52
+ it is only an icon), places, animates, and gives the 44px touch area —
53
+ the ring past the button's edge is the box's, and a press there is handed
54
+ to the button. The box sets the button's height over the host's own.
55
+ - `KitButtonProps` is the contract: `kitRole`, `variant`, `size`, `shape`
56
+ (`square`, `pill`, `default`), `icon`, `iconRight`, `children`,
57
+ `disabled`, `loading`, `pressed`, `type="button"`, the handlers, the
58
+ `aria-*` and `data-ick-*` attributes, and the ref. `kitRole` rather than
59
+ `role`, because a button that spread a `role` of `"send"` onto itself
60
+ would be an ARIA error — the ARIA `role` still arrives where there is one,
61
+ `menuitem` in the header's menu.
62
+ - The header's menu items go through it too. Its back control and an
63
+ action with an `href` are links and stay `<a>`.
64
+ - In development, a host's button that does not forward its ref, is not a
65
+ `<button>`, drops the `aria-*` or `data-ick-*` it was given, or looks no
66
+ different with keyboard focus is named in the console, once per role.
67
+ `process.env.NODE_ENV === "production"` returns before any of it.
68
+ - **`--ick-composer-control`**: the composer's send glyph, "+" and
69
+ microphone, which were 28px written into the component — four widths
70
+ Motion springs open, and an overlay offset by 36 and 44 — are
71
+ `--ick-control-s` now, and a host can move them on their own.
72
+ - **The composer's corner is concentric with its buttons**: `--ick-input-radius`
73
+ is `--ick-btn-radius` plus the surface's 8px padding, up to
74
+ `--ick-radius-xl`. A pill button leaves it at the 24 it was; a host's 6px
75
+ button gets a 14px composer instead of a 6px button in a 24px pill.
76
+ - **`BringYourOwnButton` stories** — the chat and the hand-assembled pieces
77
+ with a shadcn-style button (a 6px corner, 36px tall, its own focus ring),
78
+ and the same pieces with nothing provided — and "Bring your own button" in
79
+ theming.md: tokens, then per-role CSS, then the component.
80
+
81
+ ### Fixed
82
+
83
+ - **On a touch screen the "+" and the microphone are round.** Their boxes
84
+ were 28 written in and their buttons as wide as `size="s"`, which the touch
85
+ block raises to 32: a 32 × 28 oval beside a 32 × 32 send, overflowing its
86
+ slot by 4px. All three are 32 × 32 there now, in 32px slots. On a pointer
87
+ nothing moves.
88
+
11
89
  ## 0.60.0 — 2026-10-15
12
90
 
13
91
  Reported from a host running the kit in production, each one worked around
package/README.md CHANGED
@@ -1472,6 +1472,18 @@ existing call sites keep working. Its `s` / `m` / `l` map to `m` / `l` / `xl`.
1472
1472
  Neither takes a dark-mode prop: the theme is a token swap on an ancestor. See
1473
1473
  [theming.md](./theming.md).
1474
1474
 
1475
+ **Your own button.** `components={{ Button }}` on `ChatExperience` — or
1476
+ `ComponentsProvider` around pieces assembled by hand — draws your button
1477
+ wherever the kit draws one, except the composer's send / stop / save glyph,
1478
+ which morphs inside its button and stays the kit's. The kit keeps the layout
1479
+ (the box, its size from `--ick-control-*`, motion, the 44px touch area) and
1480
+ your button paints. It must forward its `ref`, render a `<button>` and spread
1481
+ the `aria-*` and `data-*` it is given; the props it receives are
1482
+ `KitButtonProps`, and `kitRole` says what each one is for. Set
1483
+ `--ick-btn-radius` to your corner too: the kit's glyph and the composer around
1484
+ it follow it. Tokens first, per-role CSS second, the component third — see
1485
+ "Bring your own button" in [theming.md](./theming.md#bring-your-own-button).
1486
+
1475
1487
  ## Labels: every word the kit says
1476
1488
 
1477
1489
  Every component takes a `labels` prop, and `ChatExperience` and `ChatTurnRow`
@@ -0,0 +1,28 @@
1
+ import { CSSProperties } from 'react';
2
+ import { ButtonProps } from './Button';
3
+ import { KitButtonProps, KitButtonRole, KitButtonShape } from '../components/components';
4
+ export interface KitButtonUse extends Omit<ButtonProps, "type"> {
5
+ /** What it is for. Written to `data-ick-role`, and handed to a host's button. */
6
+ kitRole: Exclude<KitButtonRole, "send" | "stop" | "save">;
7
+ /** Worked out from the content when not given: an icon alone is `square`. */
8
+ shape?: KitButtonShape;
9
+ /** The layout box a host's button sits in. The kit's own button ignores it. */
10
+ boxClassName?: string;
11
+ boxStyle?: CSSProperties;
12
+ /** Given to a host's button instead of what the kit's own is given. */
13
+ hostProps?: Partial<Pick<KitButtonProps, "icon" | "iconRight" | "children">>;
14
+ }
15
+ /**
16
+ * Every button the kit draws but the composer's morphing glyph.
17
+ *
18
+ * With no host button in `ComponentsProvider` this is the kit's `Button`,
19
+ * given exactly what it was given before the slot existed — the same element,
20
+ * the same attributes, so nothing about the default changes.
21
+ *
22
+ * With one, the host's button is drawn inside a box the kit lays out: the box
23
+ * has the size, takes the place in the row, and carries the 44px hit area on
24
+ * a touch screen; the host's button fills it and paints it. `className` and
25
+ * `style` here are the kit's paint and stay with the kit's button; where they
26
+ * also place it, the call site says so again as `boxClassName` / `boxStyle`.
27
+ */
28
+ export declare const KitButton: import('react').ForwardRefExoticComponent<KitButtonUse & import('react').RefAttributes<HTMLButtonElement>>;
@@ -0,0 +1,15 @@
1
+ import { RefObject } from 'react';
2
+ /** For tests: forget which roles have been warned about. */
3
+ export declare function resetHostButtonWarnings(): void;
4
+ /**
5
+ * In development, checks a host's button against the contract in
6
+ * `KitButtonProps`, and says so once per role when it falls short:
7
+ *
8
+ * - its ref is not forwarded (`forwardRef`), so the kit cannot focus it;
9
+ * - what it renders is not a `<button>`;
10
+ * - it drops the `aria-*` or `data-ick-*` the kit gave it;
11
+ * - keyboard focus changes nothing about how it looks.
12
+ *
13
+ * `role` is `undefined` for the kit's own button, which is not checked.
14
+ */
15
+ export declare function useHostButtonChecks(role: string | undefined, box: RefObject<HTMLElement | null>, element: RefObject<HTMLElement | null>, forwarded: RefObject<boolean>, given: Record<string, unknown>): void;
@@ -8,6 +8,7 @@ import { TranscribeHandler } from '../voice/useVoiceInput';
8
8
  import { CustomPart, CustomPartContext, TurnPartUpdate } from '../turnParts/turnParts';
9
9
  import { ComposerMenuItem } from '../ChatInput/AddCardsOverlay';
10
10
  import { ChatLabels } from '../labels/labels';
11
+ import { ChatComponents } from '../components/components';
11
12
  import { Answer } from '../QuestionCard/types';
12
13
  import { Decision } from '../Approval/Approval';
13
14
  /**
@@ -91,6 +92,15 @@ export interface ChatExperienceProps {
91
92
  * an inline object costs nothing — it is compared by content.
92
93
  */
93
94
  labels?: ChatLabels;
95
+ /**
96
+ * The host's own components, drawn in place of the kit's. `Button` is used
97
+ * for every button the chat draws except the composer's send / stop / save
98
+ * glyph, whose animation lives in the button. The kit keeps the layout —
99
+ * size, place, motion, hit area — and your button paints. It must forward
100
+ * its ref and render a `<button>`; see `KitButtonProps`. Keep it stable:
101
+ * declared outside a render, not inside one.
102
+ */
103
+ components?: ChatComponents;
94
104
  /**
95
105
  * The header's built-in actions. `true` (the default) is the theme toggle
96
106
  * and Share; `false` is neither; a list keeps only those named. The saved
@@ -235,4 +245,4 @@ export interface ChatExperienceProps {
235
245
  onDecideApproval?: (write: PartWriter, turnId: string, partId: string, decision: Decision) => void;
236
246
  className?: string;
237
247
  }
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;
248
+ export declare function ChatExperience({ onSend, chat: hostChat, renderPart, labels, components, 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;