@openwarelabs/ui 0.4.0 → 0.6.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.
Files changed (31) hide show
  1. package/README.md +28 -2
  2. package/dist/components/ChainOfThought/ChainOfThought.d.cts +44 -0
  3. package/dist/components/ChainOfThought/ChainOfThought.d.ts +44 -0
  4. package/dist/components/Chat/ChatConversation.d.cts +48 -0
  5. package/dist/components/Chat/ChatConversation.d.ts +48 -0
  6. package/dist/components/Chat/ChatLoader.d.cts +21 -0
  7. package/dist/components/Chat/ChatLoader.d.ts +21 -0
  8. package/dist/components/Chat/ChatMessage.d.cts +93 -0
  9. package/dist/components/Chat/ChatMessage.d.ts +93 -0
  10. package/dist/components/ChatSource/ChatSource.d.cts +58 -0
  11. package/dist/components/ChatSource/ChatSource.d.ts +58 -0
  12. package/dist/components/ChatTool/ChatTool.d.cts +89 -0
  13. package/dist/components/ChatTool/ChatTool.d.ts +89 -0
  14. package/dist/components/PromptInput/PromptInput.d.cts +95 -0
  15. package/dist/components/PromptInput/PromptInput.d.ts +95 -0
  16. package/dist/components/PromptInput/PromptSuggestion.d.cts +36 -0
  17. package/dist/components/PromptInput/PromptSuggestion.d.ts +36 -0
  18. package/dist/components/Prose/Prose.d.cts +22 -0
  19. package/dist/components/Prose/Prose.d.ts +22 -0
  20. package/dist/components/TextShimmer/TextShimmer.d.cts +20 -0
  21. package/dist/components/TextShimmer/TextShimmer.d.ts +20 -0
  22. package/dist/components.css +2524 -0
  23. package/dist/index.cjs +1548 -73
  24. package/dist/index.cjs.map +4 -4
  25. package/dist/index.d.cts +20 -0
  26. package/dist/index.d.ts +20 -0
  27. package/dist/index.js +1573 -72
  28. package/dist/index.js.map +4 -4
  29. package/dist/openware-ui.iife.js +7 -6
  30. package/dist/styles.css +2524 -0
  31. package/package.json +9 -3
package/README.md CHANGED
@@ -50,11 +50,11 @@ import { Button, Field, Input, Toaster, toast } from "@openwarelabs/ui";
50
50
 
51
51
  ## Components
52
52
 
53
- 83 components, each with a page in the docs.
53
+ 93 components, each with a page in the docs.
54
54
 
55
55
  | Group | Components |
56
56
  | --- | --- |
57
- | Typography | `Text` (each style on the Typography page as a variant: hero to caption, code and kbd; tones, `truncate`, `as`), `Link`, `Kbd`, `Code`, `CodeBlock`, `Snippet` |
57
+ | Typography | `Text` (each style on the Typography page as a variant: hero to caption, code and kbd; tones, `truncate`, `as`), `Link`, `Kbd`, `Code`, `CodeBlock`, `Snippet`, `Prose` (styles rendered Markdown or HTML, at chat or article size) |
58
58
  | Actions | `Button` (primary, secondary, ghost, danger; sm, md, lg; `loading`; `shape="pill"`), `buttonClassName()` for links, `ButtonGroup`, `CloseButton`, `CopyButton`, `ToggleButton`, `SegmentedControl`, `ToggleButtonGroup` (one or several on), `Toolbar` + `ToolbarSeparator` (arrow keys across its controls) |
59
59
  | Inputs | `Form` (the browser's checks, your own and the server's errors, under each field; `useFormStatus`), `Field`, `Fieldset`, `Input` (start and end content), `Textarea` (`autoGrow`), `NumberField`, `SearchField`, `InputOTP`, `ColorField`, `DateField` and `TimeField` (typed in segments, in the locale's order) |
60
60
  | Choices | `Checkbox`, `CheckboxGroup`, `RadioGroup` (plain or cards), `Switch`, `Select`, `Combobox`, `Slider` (one value or a range), `ChoiceCard`, `ListBox` (one or many, on the page) |
@@ -63,11 +63,25 @@ import { Button, Field, Input, Toaster, toast } from "@openwarelabs/ui";
63
63
  | Overlays | `Dialog`, `ConfirmDialog`, `Drawer`, `Popover`, `Menu` + `MenuItem`, `CommandPalette` |
64
64
  | Navigation | `Tabs`, `Accordion`, `Disclosure`, `Breadcrumbs`, `Pagination`, `Steps`, `NavList` + `NavItem` |
65
65
  | Data display | `Table` (sorting, selection), `Stat`, `FileTree`, `EmptyState`, `Tag`, `ColorSwatch`, `Avatar`, `AvatarGroup`, `User` |
66
+ | AI | `PromptInput` (attachments, stop while streaming) + `PromptSuggestion`, `ChatConversation` (follows the newest message, jump to latest), `ChatMessage` + `ChatMessageActions`, `ChatLoader`, `ChatTool` (a tool call with its status, input, output and approval), `ChainOfThought`, `ChatSource` + `ChatSources`, `TextShimmer` |
66
67
  | Layout | `Card`, `SettingsRow`, `Stack`, `Separator`, `ScrollShadow`, `VisuallyHidden` |
67
68
  | Marketing | `SiteHeader`, `SectionHeading`, `Chip`, `DotGrid` |
68
69
 
69
70
  Hooks for your own components: `useControllableState`, `useFloating` (top-layer positioning that flips), `usePresence` (keeps a layer mounted while it animates out), `useDismiss`, `useModalDialog`, `useMenuKeyboard`. Every prop is typed and described, so your editor explains it on hover.
70
71
 
72
+ ## Use with AI
73
+
74
+ The MCP server, `@openwarelabs/ui-mcp`, gives Claude Code, Codex, Cursor and other AI tools the setup, every component's examples and props, every block's code and the tokens, so they build with the real components:
75
+
76
+ ```sh
77
+ claude mcp add openware-ui -- npx -y @openwarelabs/ui-mcp # Claude Code
78
+ codex mcp add openware-ui -- npx -y @openwarelabs/ui-mcp # Codex
79
+ ```
80
+
81
+ Cursor and most other clients take `{ "mcpServers": { "openware-ui": { "command": "npx", "args": ["-y", "@openwarelabs/ui-mcp"] } } }`. [mcp/README.md](mcp/README.md) has every tool and client.
82
+
83
+ In the docs, every component and block has a **Copy for AI** button: a prompt with its whole reference (examples and props, or a block's two files) to paste into any AI tool. The built docs also have `llms.txt`, `llms-full.txt` and a Markdown copy of every page under `ai/`. `npm run mcp:build` rebuilds the server from the docs.
84
+
71
85
  ## Tokens
72
86
 
73
87
  Every colour, space, radius, shadow and weight is a CSS variable named `--ow-…` (`var(--ow-surface)`, `var(--ow-radius-container)`), and every text style is a class (`ow-type-section-title`, `ow-type-lead`). `tokens/tokens.json` is the single source; `npm run tokens` regenerates the CSS and `npm run contrast` checks every text and focus pair in both themes.
@@ -90,6 +104,18 @@ The docs' header has a colour picker that does this live, and the Theming page s
90
104
 
91
105
  **No bundler?** `dist/openware-ui.iife.js` sets `window.OpenwareUI`, reading React from `window.React`. A CDN serves it straight from npm: `https://cdn.jsdelivr.net/npm/@openwarelabs/ui/dist/openware-ui.iife.js`, with `dist/styles.css` beside it.
92
106
 
107
+ ## Releasing
108
+
109
+ ```sh
110
+ npm run release # works out the version, checks, tags and pushes
111
+ npm run release -- --dry-run # shows the version and the notes, changes nothing
112
+ npm run release -- minor # forces patch, minor, major or an exact version
113
+ ```
114
+
115
+ The version comes from the commit subjects since the last release, written as [Conventional Commits](https://www.conventionalcommits.org): `feat:` makes a minor release, `fix:` and `perf:` a patch, and `feat!:` or a `BREAKING CHANGE:` line a major one (a minor one before 1.0). `chore:`, `docs:`, `refactor:`, `test:`, `ci:`, `build:` and `style:` don't release on their own. The command checks that `main` is clean and up to date, runs every check and build, bumps both packages, writes `CHANGELOG.md`, commits, tags `vX.Y.Z` and pushes.
116
+
117
+ The tag starts `.github/workflows/release.yml`, which runs the checks again, publishes `@openwarelabs/ui` and `@openwarelabs/ui-mcp` to npm with trusted publishing (no token, with provenance), and makes the GitHub release from the changelog. CI checks every pull request's commit subjects against the convention; `npm run commits:check` does the same locally.
118
+
93
119
  ## Rules worth keeping
94
120
 
95
121
  - One primary button per view. Everything else is secondary or ghost.
@@ -0,0 +1,44 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ /** Where the reasoning, or one step of it, has got to. */
3
+ export type ChainOfThoughtStatus = "pending" | "running" | "success" | "error";
4
+ export interface ChainOfThoughtStep {
5
+ /** A stable key for the step, so it keeps its place as steps are added. */
6
+ id: string;
7
+ /** The step in words: "Find where invoice dates are formatted". */
8
+ title: ReactNode;
9
+ /** Where the step has got to: a hollow ring, a spinner, a check or a cross. */
10
+ status: ChainOfThoughtStatus;
11
+ /** More about the step under its title: what it found, a file path, a short list. */
12
+ detail?: ReactNode;
13
+ /** A 12px icon in place of the check or the hollow ring, e.g. a search icon. Running and failed steps keep their own mark. */
14
+ icon?: ReactNode;
15
+ }
16
+ export interface ChainOfThoughtProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
17
+ /** The plan, in order, each with its status. */
18
+ steps?: ChainOfThoughtStep[];
19
+ /** "running" shows "Thinking…" with a shimmer and keeps it open; anything else is finished.
20
+ * @default "success" */
21
+ status?: ChainOfThoughtStatus;
22
+ /** How long it thought, in milliseconds: "Thought for 12 s". */
23
+ duration?: number;
24
+ /** The header's words. Defaults to "Thinking…" while running and "Thought for 12 s" once done. */
25
+ label?: ReactNode;
26
+ /** The reasoning itself, as text, shown above the steps. It can grow while it streams in. */
27
+ children?: ReactNode;
28
+ /** Whether the reasoning shows. Leave out to let it follow `status`. */
29
+ expanded?: boolean;
30
+ /** Whether it starts open. Defaults to open while running and closed once done. */
31
+ defaultExpanded?: boolean;
32
+ /** Called with the new state when it opens or closes, by the person or by a change of status. */
33
+ onExpandedChange?(expanded: boolean): void;
34
+ /** Folds the reasoning away when `status` turns from "running" to done, unless the person has opened or closed it.
35
+ * @default true */
36
+ collapseWhenDone?: boolean;
37
+ /** The locale for the duration's digits, e.g. "de-CH". Defaults to the browser's. */
38
+ locale?: string;
39
+ }
40
+ /**
41
+ * An agent's reasoning and plan: a "Thinking…" header that opens to the reasoning and a list of steps on a thin line,
42
+ * each with its status. It stays open while the agent works and folds to "Thought for 12 s" when it's done.
43
+ */
44
+ export declare const ChainOfThought: import("react").ForwardRefExoticComponent<ChainOfThoughtProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,44 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ /** Where the reasoning, or one step of it, has got to. */
3
+ export type ChainOfThoughtStatus = "pending" | "running" | "success" | "error";
4
+ export interface ChainOfThoughtStep {
5
+ /** A stable key for the step, so it keeps its place as steps are added. */
6
+ id: string;
7
+ /** The step in words: "Find where invoice dates are formatted". */
8
+ title: ReactNode;
9
+ /** Where the step has got to: a hollow ring, a spinner, a check or a cross. */
10
+ status: ChainOfThoughtStatus;
11
+ /** More about the step under its title: what it found, a file path, a short list. */
12
+ detail?: ReactNode;
13
+ /** A 12px icon in place of the check or the hollow ring, e.g. a search icon. Running and failed steps keep their own mark. */
14
+ icon?: ReactNode;
15
+ }
16
+ export interface ChainOfThoughtProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
17
+ /** The plan, in order, each with its status. */
18
+ steps?: ChainOfThoughtStep[];
19
+ /** "running" shows "Thinking…" with a shimmer and keeps it open; anything else is finished.
20
+ * @default "success" */
21
+ status?: ChainOfThoughtStatus;
22
+ /** How long it thought, in milliseconds: "Thought for 12 s". */
23
+ duration?: number;
24
+ /** The header's words. Defaults to "Thinking…" while running and "Thought for 12 s" once done. */
25
+ label?: ReactNode;
26
+ /** The reasoning itself, as text, shown above the steps. It can grow while it streams in. */
27
+ children?: ReactNode;
28
+ /** Whether the reasoning shows. Leave out to let it follow `status`. */
29
+ expanded?: boolean;
30
+ /** Whether it starts open. Defaults to open while running and closed once done. */
31
+ defaultExpanded?: boolean;
32
+ /** Called with the new state when it opens or closes, by the person or by a change of status. */
33
+ onExpandedChange?(expanded: boolean): void;
34
+ /** Folds the reasoning away when `status` turns from "running" to done, unless the person has opened or closed it.
35
+ * @default true */
36
+ collapseWhenDone?: boolean;
37
+ /** The locale for the duration's digits, e.g. "de-CH". Defaults to the browser's. */
38
+ locale?: string;
39
+ }
40
+ /**
41
+ * An agent's reasoning and plan: a "Thinking…" header that opens to the reasoning and a list of steps on a thin line,
42
+ * each with its status. It stays open while the agent works and folds to "Thought for 12 s" when it's done.
43
+ */
44
+ export declare const ChainOfThought: import("react").ForwardRefExoticComponent<ChainOfThoughtProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,48 @@
1
+ import { type HTMLAttributes, type MutableRefObject, type ReactNode } from "react";
2
+ export interface ChatConversationProps extends HTMLAttributes<HTMLDivElement> {
3
+ /** The messages: ChatMessages, a ChatLoader, anything else in the column. */
4
+ children?: ReactNode;
5
+ /** Shown in place of the messages while there are none: a greeting, suggestions, an EmptyState. */
6
+ empty?: ReactNode;
7
+ /** Docked under the messages and lined up with them, outside the log: usually a PromptInput. It stays in view while
8
+ * the messages scroll, and Jump to latest floats above it. */
9
+ footer?: ReactNode;
10
+ /** Names the conversation for screen readers.
11
+ * @default "Conversation" */
12
+ "aria-label"?: string;
13
+ /** The id of a visible heading that names the conversation, in place of aria-label. */
14
+ "aria-labelledby"?: string;
15
+ /** `comfortable` keeps the messages in a 720px column in the middle, for reading; `full` lets them use the whole
16
+ * width, for a narrow side panel.
17
+ * @default "comfortable" */
18
+ width?: "comfortable" | "full";
19
+ /** The words on the button that appears once the reader scrolls up.
20
+ * @default "Jump to latest" */
21
+ jumpLabel?: string;
22
+ /** How that button counts the messages that arrived while the reader was scrolled up.
23
+ * @default (count) => `${count} new` */
24
+ formatNewCount?(count: number): string;
25
+ }
26
+ export interface ChatConversationHandle {
27
+ /** Scrolls to the newest message and follows new content again: smoothly, or at once under reduced motion. Call it
28
+ * when the person sends a message, so they see their own words and the answer. */
29
+ scrollToBottom(options?: {
30
+ behavior?: ScrollBehavior;
31
+ }): void;
32
+ }
33
+ /** What messages inside a conversation need from it. */
34
+ interface ChatConversationContextValue {
35
+ /** True once the first messages have painted: messages that mount after that animate in. */
36
+ ready: MutableRefObject<boolean>;
37
+ /** A message mounted; it counts as new while the reader is scrolled up. Returns the cleanup for when it goes. */
38
+ register(id: string): () => void;
39
+ }
40
+ /** @internal */
41
+ export declare const ChatConversationContext: import("react").Context<ChatConversationContextValue | null>;
42
+ /**
43
+ * The scrolling column of a chat. It follows the newest content while the reader is at the bottom (new messages, an
44
+ * answer that's still streaming), stops as soon as they scroll up to read, and offers a "Jump to latest" button to
45
+ * come back. It's a polite live region (role="log"), so screen readers hear messages as they're added.
46
+ */
47
+ export declare const ChatConversation: import("react").ForwardRefExoticComponent<ChatConversationProps & import("react").RefAttributes<ChatConversationHandle>>;
48
+ export {};
@@ -0,0 +1,48 @@
1
+ import { type HTMLAttributes, type MutableRefObject, type ReactNode } from "react";
2
+ export interface ChatConversationProps extends HTMLAttributes<HTMLDivElement> {
3
+ /** The messages: ChatMessages, a ChatLoader, anything else in the column. */
4
+ children?: ReactNode;
5
+ /** Shown in place of the messages while there are none: a greeting, suggestions, an EmptyState. */
6
+ empty?: ReactNode;
7
+ /** Docked under the messages and lined up with them, outside the log: usually a PromptInput. It stays in view while
8
+ * the messages scroll, and Jump to latest floats above it. */
9
+ footer?: ReactNode;
10
+ /** Names the conversation for screen readers.
11
+ * @default "Conversation" */
12
+ "aria-label"?: string;
13
+ /** The id of a visible heading that names the conversation, in place of aria-label. */
14
+ "aria-labelledby"?: string;
15
+ /** `comfortable` keeps the messages in a 720px column in the middle, for reading; `full` lets them use the whole
16
+ * width, for a narrow side panel.
17
+ * @default "comfortable" */
18
+ width?: "comfortable" | "full";
19
+ /** The words on the button that appears once the reader scrolls up.
20
+ * @default "Jump to latest" */
21
+ jumpLabel?: string;
22
+ /** How that button counts the messages that arrived while the reader was scrolled up.
23
+ * @default (count) => `${count} new` */
24
+ formatNewCount?(count: number): string;
25
+ }
26
+ export interface ChatConversationHandle {
27
+ /** Scrolls to the newest message and follows new content again: smoothly, or at once under reduced motion. Call it
28
+ * when the person sends a message, so they see their own words and the answer. */
29
+ scrollToBottom(options?: {
30
+ behavior?: ScrollBehavior;
31
+ }): void;
32
+ }
33
+ /** What messages inside a conversation need from it. */
34
+ interface ChatConversationContextValue {
35
+ /** True once the first messages have painted: messages that mount after that animate in. */
36
+ ready: MutableRefObject<boolean>;
37
+ /** A message mounted; it counts as new while the reader is scrolled up. Returns the cleanup for when it goes. */
38
+ register(id: string): () => void;
39
+ }
40
+ /** @internal */
41
+ export declare const ChatConversationContext: import("react").Context<ChatConversationContextValue | null>;
42
+ /**
43
+ * The scrolling column of a chat. It follows the newest content while the reader is at the bottom (new messages, an
44
+ * answer that's still streaming), stops as soon as they scroll up to read, and offers a "Jump to latest" button to
45
+ * come back. It's a polite live region (role="log"), so screen readers hear messages as they're added.
46
+ */
47
+ export declare const ChatConversation: import("react").ForwardRefExoticComponent<ChatConversationProps & import("react").RefAttributes<ChatConversationHandle>>;
48
+ export {};
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ChatLoaderProps {
3
+ /** Words in place of the dots, with a shimmer across them: "Thinking…", "Reading the codebase…". */
4
+ label?: ReactNode;
5
+ /** Just the dots or the label, without a message's avatar and spacing: for inside a message, a step or a status bar.
6
+ * @default false */
7
+ inline?: boolean;
8
+ /** The agent's name above, as on its messages: "Acme agent". */
9
+ name?: ReactNode;
10
+ /** The agent's bot tile by default; any node in its place, or null for none. */
11
+ avatar?: ReactNode;
12
+ /** What screen readers hear while the dots show. With a `label`, they hear the label.
13
+ * @default "Working…" */
14
+ srLabel?: string;
15
+ className?: string;
16
+ }
17
+ /**
18
+ * The agent is working and nothing has arrived yet: three dots that pulse in turn, or a shimmering label such as
19
+ * "Thinking…". It sits in the conversation like the agent's next message, so the answer takes its place without a jump.
20
+ */
21
+ export declare function ChatLoader({ label, inline, name, avatar, srLabel, className }: ChatLoaderProps): import("react").JSX.Element;
@@ -0,0 +1,21 @@
1
+ import type { ReactNode } from "react";
2
+ export interface ChatLoaderProps {
3
+ /** Words in place of the dots, with a shimmer across them: "Thinking…", "Reading the codebase…". */
4
+ label?: ReactNode;
5
+ /** Just the dots or the label, without a message's avatar and spacing: for inside a message, a step or a status bar.
6
+ * @default false */
7
+ inline?: boolean;
8
+ /** The agent's name above, as on its messages: "Acme agent". */
9
+ name?: ReactNode;
10
+ /** The agent's bot tile by default; any node in its place, or null for none. */
11
+ avatar?: ReactNode;
12
+ /** What screen readers hear while the dots show. With a `label`, they hear the label.
13
+ * @default "Working…" */
14
+ srLabel?: string;
15
+ className?: string;
16
+ }
17
+ /**
18
+ * The agent is working and nothing has arrived yet: three dots that pulse in turn, or a shimmering label such as
19
+ * "Thinking…". It sits in the conversation like the agent's next message, so the answer takes its place without a jump.
20
+ */
21
+ export declare function ChatLoader({ label, inline, name, avatar, srLabel, className }: ChatLoaderProps): import("react").JSX.Element;
@@ -0,0 +1,93 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ export interface ChatAgentAvatarProps {
3
+ /** sm 24px, md 28px beside a message, lg 36px in a page or panel header: the sizes of Avatar.
4
+ * @default "md" */
5
+ size?: "sm" | "md" | "lg";
6
+ /** Give it when the avatar stands alone; beside the agent's name, leave it out and it's hidden from screen readers. */
7
+ label?: string;
8
+ className?: string;
9
+ }
10
+ /**
11
+ * The agent's avatar: a bot icon on a tinted circle, in the sizes of Avatar. Messages and the loader show it by
12
+ * default; use it wherever else the agent appears, such as a chat's header.
13
+ */
14
+ export declare function ChatAgentAvatar({ size, label, className }: ChatAgentAvatarProps): import("react").JSX.Element;
15
+ /**
16
+ * Whether this message or loader should animate in: only when it arrives in a conversation that's already showing,
17
+ * not for the history it opens with.
18
+ * @internal
19
+ */
20
+ export declare function useChatEntrance(): boolean;
21
+ export type ChatMessageFrom = "user" | "assistant" | "system";
22
+ export type ChatMessageStatus = "streaming" | "done" | "error";
23
+ export interface ChatMessageProps extends Omit<HTMLAttributes<HTMLElement>, "children"> {
24
+ /** Who it's from. `user`: the person, in a bubble on the right. `assistant`: the agent, full width beside its
25
+ * avatar. `system`: a muted line in the middle, between hairlines, for things that happened ("Maya changed the
26
+ * model to Claude Sonnet"). */
27
+ from: ChatMessageFrom;
28
+ /** The message. A person's plain text keeps its line breaks; give an agent's formatted answer as Prose, and put
29
+ * paragraphs in Text so blocks sit 12px apart. */
30
+ children?: ReactNode;
31
+ /** Shown above the message: "Acme agent", "Maya Chen". Left out, screen readers hear "You" or "Agent" instead. */
32
+ name?: ReactNode;
33
+ /** The agent's bot tile by default; any node (an Avatar) in its place, or null for none. A person's message shows one
34
+ * only when you give it. */
35
+ avatar?: ReactNode;
36
+ /** When it was sent, as a Date or an ISO string. It shows as a short local time, with the full date on hover. */
37
+ time?: Date | string;
38
+ /** The locale for the time, such as "en-GB". Defaults to the browser's. */
39
+ locale?: string;
40
+ /** `streaming` while the text is still arriving: a soft caret blinks after it and the message is aria-busy.
41
+ * `error` shows a line saying what went wrong, with Retry when you pass `onRetry`.
42
+ * @default "done" */
43
+ status?: ChatMessageStatus;
44
+ /** What went wrong, for `status="error"`.
45
+ * @default "The answer didn't finish." for the agent, "Couldn't send this message." for a person */
46
+ error?: ReactNode;
47
+ /** Shows a Retry button on the error line. */
48
+ onRetry?(): void;
49
+ /** The Retry button's words, for another language.
50
+ * @default "Retry" */
51
+ retryLabel?: string;
52
+ /** Actions under the message, usually a ChatMessageActions. They wait until the message has finished streaming. */
53
+ actions?: ReactNode;
54
+ /** `hover` shows the actions while the message is hovered or has focus, and always on touch screens. `always` keeps
55
+ * them in view, for the latest answer.
56
+ * @default "hover" */
57
+ showActions?: "hover" | "always";
58
+ }
59
+ /**
60
+ * One message in a conversation: the person's in a bubble on the right, the agent's across the full width with its
61
+ * avatar, name and time, or a system line in the middle. It shows a caret while the answer streams, an error with
62
+ * Retry when it fails, and its actions on hover.
63
+ */
64
+ export declare const ChatMessage: import("react").ForwardRefExoticComponent<ChatMessageProps & import("react").RefAttributes<HTMLElement>>;
65
+ export type ChatRating = "up" | "down";
66
+ export interface ChatMessageActionsProps {
67
+ /** The message as plain text. Shows Copy, which copies it and says "Copied" for two seconds. */
68
+ copyText?: string;
69
+ /** Shows Edit, for the person's own messages. */
70
+ onEdit?(): void;
71
+ /** Shows Retry, to ask for the answer again. */
72
+ onRetry?(): void;
73
+ /** Shows Good response and Bad response. Called with the one pressed, or null when the pressed one is pressed again. */
74
+ onRate?(rating: ChatRating | null): void;
75
+ /** Which rating is pressed, when you keep it yourself. */
76
+ rating?: ChatRating | null;
77
+ /** The starting rating when uncontrolled.
78
+ * @default null */
79
+ defaultRating?: ChatRating | null;
80
+ /** More actions after these: ghost, sm, icon-only Buttons, each in a Tooltip with its aria-label. */
81
+ children?: ReactNode;
82
+ /** The buttons' words, for another language. */
83
+ labels?: Partial<Record<"copy" | "copied" | "edit" | "retry" | "good" | "bad", string>>;
84
+ /** Names the toolbar for screen readers.
85
+ * @default "Message actions" */
86
+ "aria-label"?: string;
87
+ className?: string;
88
+ }
89
+ /**
90
+ * The row of small actions under a message: Copy, Edit, Retry, and Good or Bad response. Only the ones you give a
91
+ * callback (or `copyText`) appear. It's a toolbar: one tab stop, with the arrow keys moving between the buttons.
92
+ */
93
+ export declare function ChatMessageActions({ copyText: text, onEdit, onRetry, onRate, rating, defaultRating, children, labels, "aria-label": ariaLabel, className, }: ChatMessageActionsProps): import("react").JSX.Element;
@@ -0,0 +1,93 @@
1
+ import { type HTMLAttributes, type ReactNode } from "react";
2
+ export interface ChatAgentAvatarProps {
3
+ /** sm 24px, md 28px beside a message, lg 36px in a page or panel header: the sizes of Avatar.
4
+ * @default "md" */
5
+ size?: "sm" | "md" | "lg";
6
+ /** Give it when the avatar stands alone; beside the agent's name, leave it out and it's hidden from screen readers. */
7
+ label?: string;
8
+ className?: string;
9
+ }
10
+ /**
11
+ * The agent's avatar: a bot icon on a tinted circle, in the sizes of Avatar. Messages and the loader show it by
12
+ * default; use it wherever else the agent appears, such as a chat's header.
13
+ */
14
+ export declare function ChatAgentAvatar({ size, label, className }: ChatAgentAvatarProps): import("react").JSX.Element;
15
+ /**
16
+ * Whether this message or loader should animate in: only when it arrives in a conversation that's already showing,
17
+ * not for the history it opens with.
18
+ * @internal
19
+ */
20
+ export declare function useChatEntrance(): boolean;
21
+ export type ChatMessageFrom = "user" | "assistant" | "system";
22
+ export type ChatMessageStatus = "streaming" | "done" | "error";
23
+ export interface ChatMessageProps extends Omit<HTMLAttributes<HTMLElement>, "children"> {
24
+ /** Who it's from. `user`: the person, in a bubble on the right. `assistant`: the agent, full width beside its
25
+ * avatar. `system`: a muted line in the middle, between hairlines, for things that happened ("Maya changed the
26
+ * model to Claude Sonnet"). */
27
+ from: ChatMessageFrom;
28
+ /** The message. A person's plain text keeps its line breaks; give an agent's formatted answer as Prose, and put
29
+ * paragraphs in Text so blocks sit 12px apart. */
30
+ children?: ReactNode;
31
+ /** Shown above the message: "Acme agent", "Maya Chen". Left out, screen readers hear "You" or "Agent" instead. */
32
+ name?: ReactNode;
33
+ /** The agent's bot tile by default; any node (an Avatar) in its place, or null for none. A person's message shows one
34
+ * only when you give it. */
35
+ avatar?: ReactNode;
36
+ /** When it was sent, as a Date or an ISO string. It shows as a short local time, with the full date on hover. */
37
+ time?: Date | string;
38
+ /** The locale for the time, such as "en-GB". Defaults to the browser's. */
39
+ locale?: string;
40
+ /** `streaming` while the text is still arriving: a soft caret blinks after it and the message is aria-busy.
41
+ * `error` shows a line saying what went wrong, with Retry when you pass `onRetry`.
42
+ * @default "done" */
43
+ status?: ChatMessageStatus;
44
+ /** What went wrong, for `status="error"`.
45
+ * @default "The answer didn't finish." for the agent, "Couldn't send this message." for a person */
46
+ error?: ReactNode;
47
+ /** Shows a Retry button on the error line. */
48
+ onRetry?(): void;
49
+ /** The Retry button's words, for another language.
50
+ * @default "Retry" */
51
+ retryLabel?: string;
52
+ /** Actions under the message, usually a ChatMessageActions. They wait until the message has finished streaming. */
53
+ actions?: ReactNode;
54
+ /** `hover` shows the actions while the message is hovered or has focus, and always on touch screens. `always` keeps
55
+ * them in view, for the latest answer.
56
+ * @default "hover" */
57
+ showActions?: "hover" | "always";
58
+ }
59
+ /**
60
+ * One message in a conversation: the person's in a bubble on the right, the agent's across the full width with its
61
+ * avatar, name and time, or a system line in the middle. It shows a caret while the answer streams, an error with
62
+ * Retry when it fails, and its actions on hover.
63
+ */
64
+ export declare const ChatMessage: import("react").ForwardRefExoticComponent<ChatMessageProps & import("react").RefAttributes<HTMLElement>>;
65
+ export type ChatRating = "up" | "down";
66
+ export interface ChatMessageActionsProps {
67
+ /** The message as plain text. Shows Copy, which copies it and says "Copied" for two seconds. */
68
+ copyText?: string;
69
+ /** Shows Edit, for the person's own messages. */
70
+ onEdit?(): void;
71
+ /** Shows Retry, to ask for the answer again. */
72
+ onRetry?(): void;
73
+ /** Shows Good response and Bad response. Called with the one pressed, or null when the pressed one is pressed again. */
74
+ onRate?(rating: ChatRating | null): void;
75
+ /** Which rating is pressed, when you keep it yourself. */
76
+ rating?: ChatRating | null;
77
+ /** The starting rating when uncontrolled.
78
+ * @default null */
79
+ defaultRating?: ChatRating | null;
80
+ /** More actions after these: ghost, sm, icon-only Buttons, each in a Tooltip with its aria-label. */
81
+ children?: ReactNode;
82
+ /** The buttons' words, for another language. */
83
+ labels?: Partial<Record<"copy" | "copied" | "edit" | "retry" | "good" | "bad", string>>;
84
+ /** Names the toolbar for screen readers.
85
+ * @default "Message actions" */
86
+ "aria-label"?: string;
87
+ className?: string;
88
+ }
89
+ /**
90
+ * The row of small actions under a message: Copy, Edit, Retry, and Good or Bad response. Only the ones you give a
91
+ * callback (or `copyText`) appear. It's a toolbar: one tab stop, with the arrow keys moving between the buttons.
92
+ */
93
+ export declare function ChatMessageActions({ copyText: text, onEdit, onRetry, onRate, rating, defaultRating, children, labels, "aria-label": ariaLabel, className, }: ChatMessageActionsProps): import("react").JSX.Element;
@@ -0,0 +1,58 @@
1
+ import { type HTMLAttributes, type MouseEvent, type ReactNode } from "react";
2
+ /** One source an answer drew on: a file, a pull request, a web page. ChatSource and ChatSources both take it. */
3
+ export interface ChatSourceItem {
4
+ /** What it is: a function, a pull request's title, a page's title. */
5
+ title: ReactNode;
6
+ /** Where it is, in one line: "src/billing/invoice.ts · lines 40–72", "Pull request #482", "docs.stripe.com". */
7
+ source?: ReactNode;
8
+ /** A few lines from it, in the preview. ChatSources leaves it out. */
9
+ snippet?: ReactNode;
10
+ /** Where it opens. With it the citation is a link; without, a button. */
11
+ href?: string;
12
+ /** Opens `href` in a new tab, for sources outside your app.
13
+ * @default false */
14
+ external?: boolean;
15
+ /** A 15px icon for the kind of source, beside its title. */
16
+ icon?: ReactNode;
17
+ /** Its number, shown in the citation chip and on its card: [1], [2]. */
18
+ index?: number;
19
+ }
20
+ export interface ChatSourceProps extends ChatSourceItem {
21
+ /** A short name for the chip when there's no `index`: "invoice.ts", "#482". */
22
+ label?: string;
23
+ /** How a string `snippet` shows: `text` as a quote, `code` in the mono face with its line breaks kept.
24
+ * @default "text" */
25
+ snippetFormat?: "text" | "code";
26
+ /** The words of the preview's link to `href`.
27
+ * @default "Open" */
28
+ openLabel?: string;
29
+ /** Called when the chip is pressed, e.g. to show the source in a side panel. On touch, the first press opens the preview instead. */
30
+ onClick?(event: MouseEvent<HTMLElement>): void;
31
+ /** Names the chip for screen readers. Defaults to "Source 1", or the label. */
32
+ "aria-label"?: string;
33
+ /** Classes for the chip's wrapper, which also holds the preview. */
34
+ className?: string;
35
+ }
36
+ /**
37
+ * An inline citation in an answer: a small numbered chip that previews its source on hover, keyboard focus, or a tap,
38
+ * in a card above the page. The chip is a link to the source when it has an `href`; the preview describes it to screen
39
+ * readers without taking focus.
40
+ */
41
+ export declare const ChatSource: import("react").ForwardRefExoticComponent<ChatSourceProps & import("react").RefAttributes<HTMLElement>>;
42
+ export interface ChatSourcesProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
43
+ /** The sources, in the order the answer cites them. An `id` keeps each one's place as the list changes. */
44
+ sources: Array<ChatSourceItem & {
45
+ id?: string;
46
+ }>;
47
+ /** The toggle's words. Defaults to "3 sources". */
48
+ label?: ReactNode;
49
+ /** Whether the list shows. */
50
+ expanded?: boolean;
51
+ /** Whether the list starts open.
52
+ * @default false */
53
+ defaultExpanded?: boolean;
54
+ /** Called with the new state when the person opens or closes the list. */
55
+ onExpandedChange?(expanded: boolean): void;
56
+ }
57
+ /** The sources under an answer: a "3 sources" toggle that opens to a card for each, linking to it when it has an `href`. */
58
+ export declare const ChatSources: import("react").ForwardRefExoticComponent<ChatSourcesProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,58 @@
1
+ import { type HTMLAttributes, type MouseEvent, type ReactNode } from "react";
2
+ /** One source an answer drew on: a file, a pull request, a web page. ChatSource and ChatSources both take it. */
3
+ export interface ChatSourceItem {
4
+ /** What it is: a function, a pull request's title, a page's title. */
5
+ title: ReactNode;
6
+ /** Where it is, in one line: "src/billing/invoice.ts · lines 40–72", "Pull request #482", "docs.stripe.com". */
7
+ source?: ReactNode;
8
+ /** A few lines from it, in the preview. ChatSources leaves it out. */
9
+ snippet?: ReactNode;
10
+ /** Where it opens. With it the citation is a link; without, a button. */
11
+ href?: string;
12
+ /** Opens `href` in a new tab, for sources outside your app.
13
+ * @default false */
14
+ external?: boolean;
15
+ /** A 15px icon for the kind of source, beside its title. */
16
+ icon?: ReactNode;
17
+ /** Its number, shown in the citation chip and on its card: [1], [2]. */
18
+ index?: number;
19
+ }
20
+ export interface ChatSourceProps extends ChatSourceItem {
21
+ /** A short name for the chip when there's no `index`: "invoice.ts", "#482". */
22
+ label?: string;
23
+ /** How a string `snippet` shows: `text` as a quote, `code` in the mono face with its line breaks kept.
24
+ * @default "text" */
25
+ snippetFormat?: "text" | "code";
26
+ /** The words of the preview's link to `href`.
27
+ * @default "Open" */
28
+ openLabel?: string;
29
+ /** Called when the chip is pressed, e.g. to show the source in a side panel. On touch, the first press opens the preview instead. */
30
+ onClick?(event: MouseEvent<HTMLElement>): void;
31
+ /** Names the chip for screen readers. Defaults to "Source 1", or the label. */
32
+ "aria-label"?: string;
33
+ /** Classes for the chip's wrapper, which also holds the preview. */
34
+ className?: string;
35
+ }
36
+ /**
37
+ * An inline citation in an answer: a small numbered chip that previews its source on hover, keyboard focus, or a tap,
38
+ * in a card above the page. The chip is a link to the source when it has an `href`; the preview describes it to screen
39
+ * readers without taking focus.
40
+ */
41
+ export declare const ChatSource: import("react").ForwardRefExoticComponent<ChatSourceProps & import("react").RefAttributes<HTMLElement>>;
42
+ export interface ChatSourcesProps extends Omit<HTMLAttributes<HTMLDivElement>, "title"> {
43
+ /** The sources, in the order the answer cites them. An `id` keeps each one's place as the list changes. */
44
+ sources: Array<ChatSourceItem & {
45
+ id?: string;
46
+ }>;
47
+ /** The toggle's words. Defaults to "3 sources". */
48
+ label?: ReactNode;
49
+ /** Whether the list shows. */
50
+ expanded?: boolean;
51
+ /** Whether the list starts open.
52
+ * @default false */
53
+ defaultExpanded?: boolean;
54
+ /** Called with the new state when the person opens or closes the list. */
55
+ onExpandedChange?(expanded: boolean): void;
56
+ }
57
+ /** The sources under an answer: a "3 sources" toggle that opens to a card for each, linking to it when it has an `href`. */
58
+ export declare const ChatSources: import("react").ForwardRefExoticComponent<ChatSourcesProps & import("react").RefAttributes<HTMLDivElement>>;