@veevarts/design-system 2.0.0-alpha.59 → 2.0.0-alpha.60
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/README.md +22 -0
- package/dist/assistant.cjs +1 -0
- package/dist/assistant.d.ts +10 -0
- package/dist/assistant.js +790 -0
- package/dist/hasContent-BCRxiry-.js +1200 -0
- package/dist/hasContent-JfCtoDIu.cjs +1 -0
- package/dist/index.cjs +9 -9
- package/dist/index.js +6253 -7432
- package/dist/patterns/AssistantActivity/AssistantActivity.d.ts +15 -0
- package/dist/patterns/AssistantActivity/formatAssistantDuration.d.ts +10 -0
- package/dist/patterns/AssistantActivity/index.d.ts +3 -0
- package/dist/patterns/AssistantActivity/styles.d.ts +31 -0
- package/dist/patterns/AssistantActivity/types.d.ts +42 -0
- package/dist/patterns/AssistantComposer/AssistantComposer.d.ts +15 -0
- package/dist/patterns/AssistantComposer/index.d.ts +2 -0
- package/dist/patterns/AssistantComposer/styles.d.ts +33 -0
- package/dist/patterns/AssistantComposer/types.d.ts +62 -0
- package/dist/patterns/AssistantMessage/AssistantMessage.d.ts +17 -0
- package/dist/patterns/AssistantMessage/index.d.ts +2 -0
- package/dist/patterns/AssistantMessage/styles.d.ts +45 -0
- package/dist/patterns/AssistantMessage/types.d.ts +84 -0
- package/dist/patterns/AssistantNotice/AssistantNotice.d.ts +18 -0
- package/dist/patterns/AssistantNotice/index.d.ts +2 -0
- package/dist/patterns/AssistantNotice/styles.d.ts +49 -0
- package/dist/patterns/AssistantNotice/types.d.ts +58 -0
- package/dist/patterns/ScrollToLatest/ScrollToLatest.d.ts +18 -0
- package/dist/patterns/ScrollToLatest/index.d.ts +2 -0
- package/dist/patterns/ScrollToLatest/styles.d.ts +11 -0
- package/dist/patterns/ScrollToLatest/types.d.ts +36 -0
- package/dist/test/backdrop.d.ts +12 -0
- package/package.json +21 -2
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { JSX } from 'react';
|
|
2
|
+
import { AssistantActivityProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* The steps an assistant took for one turn, one row per finished step: done
|
|
5
|
+
* or failed, with how long it took. A HeroUI `Accordion` with a single item,
|
|
6
|
+
* so the group collapses under its title. Animation is off when the person
|
|
7
|
+
* prefers reduced motion.
|
|
8
|
+
*
|
|
9
|
+
* Renders nothing when `items` is empty.
|
|
10
|
+
*/
|
|
11
|
+
export declare function AssistantActivity({ items, title, titleText, isExpanded, defaultExpanded, onExpandedChange, stateLabels, formatDuration, className, 'data-testid': dataTestId, }: AssistantActivityProps): JSX.Element | null;
|
|
12
|
+
export declare namespace AssistantActivity {
|
|
13
|
+
var displayName: string;
|
|
14
|
+
}
|
|
15
|
+
export default AssistantActivity;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A short, unit-suffixed duration for activity rows and running clocks:
|
|
3
|
+
* `320 ms`, `1.2 s`, `12 s`, `2 min 05 s`.
|
|
4
|
+
*
|
|
5
|
+
* Unit words are SI symbols (`ms`, `s`) and the SI-accepted `min`, so there is
|
|
6
|
+
* nothing to translate; pass your own formatter where a locale needs a
|
|
7
|
+
* different decimal mark. Every step truncates, so a value never shows as the
|
|
8
|
+
* next unit up before it gets there (999.6 ms is `999 ms`, not `1000 ms`).
|
|
9
|
+
*/
|
|
10
|
+
export declare function formatAssistantDuration(ms: number): string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { AssistantActivityState } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Every slot class of `AssistantActivity`. Semantic HeroUI colours only, so
|
|
4
|
+
* the group flips under `.dark` with no dark values of its own.
|
|
5
|
+
*/
|
|
6
|
+
export declare const assistantActivityClass: {
|
|
7
|
+
readonly base: "w-full px-0";
|
|
8
|
+
readonly item: "shadow-none";
|
|
9
|
+
readonly trigger: "gap-2 py-1.5";
|
|
10
|
+
readonly title: "text-sm font-medium text-default-700";
|
|
11
|
+
readonly indicator: "text-default-500";
|
|
12
|
+
readonly content: "pb-2 pt-0";
|
|
13
|
+
readonly list: "m-0 flex list-none flex-col gap-1.5 p-0";
|
|
14
|
+
readonly row: "flex min-w-0 items-start gap-2 text-sm";
|
|
15
|
+
readonly icon: "mt-0.5 flex-none";
|
|
16
|
+
readonly label: "min-w-0 flex-1 text-foreground [overflow-wrap:anywhere]";
|
|
17
|
+
/**
|
|
18
|
+
* HeroUI's flat danger chip is `text-danger-600 dark:text-danger-500`,
|
|
19
|
+
* which measures 4.25:1 on its own fill in dark. The 700 step clears AA in
|
|
20
|
+
* both themes (HeroUI's dark scale is reversed, so it is a light tint
|
|
21
|
+
* there); the `dark:` repeat is what overrides HeroUI's `dark:` class.
|
|
22
|
+
*/
|
|
23
|
+
readonly failed: "flex-none text-danger-700 dark:text-danger-700";
|
|
24
|
+
readonly duration: "mt-px flex-none text-xs tabular-nums text-default-500";
|
|
25
|
+
/** "Done:" before a done row, for screen readers only. */
|
|
26
|
+
readonly stateLabel: "sr-only";
|
|
27
|
+
};
|
|
28
|
+
export declare const assistantActivityStateIcon: Record<AssistantActivityState, {
|
|
29
|
+
icon: string;
|
|
30
|
+
className: string;
|
|
31
|
+
}>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* How a step ended. There is no running state on purpose: a step is listed
|
|
4
|
+
* once it has finished, and the panel's own "working" notice carries the wait.
|
|
5
|
+
*/
|
|
6
|
+
export type AssistantActivityState = 'done' | 'failed';
|
|
7
|
+
export interface AssistantActivityItem {
|
|
8
|
+
/** Stable key; also the row's `data-testid` suffix. */
|
|
9
|
+
id: string;
|
|
10
|
+
/** What the step did, in plain words ("Searched emails · last 60 days"). */
|
|
11
|
+
label: ReactNode;
|
|
12
|
+
/** How the step ended. */
|
|
13
|
+
state: AssistantActivityState;
|
|
14
|
+
/** How long it took. Omitted, no duration is shown. */
|
|
15
|
+
durationMs?: number;
|
|
16
|
+
}
|
|
17
|
+
export interface AssistantActivityProps {
|
|
18
|
+
/** The finished steps, in the order to show. Empty, nothing renders. */
|
|
19
|
+
items: AssistantActivityItem[];
|
|
20
|
+
/** The group's heading and toggle text, e.g. "Read 4 sources". */
|
|
21
|
+
title: ReactNode;
|
|
22
|
+
/**
|
|
23
|
+
* Plain-text `title`, for type-to-select and assistive tech. Needed when
|
|
24
|
+
* `title` is not a string (React Aria warns otherwise); a string `title` is
|
|
25
|
+
* used as is.
|
|
26
|
+
*/
|
|
27
|
+
titleText?: string;
|
|
28
|
+
/** Controlled open state. */
|
|
29
|
+
isExpanded?: boolean;
|
|
30
|
+
/** Uncontrolled initial state. Defaults to open. */
|
|
31
|
+
defaultExpanded?: boolean;
|
|
32
|
+
/** Called when the group opens or closes. */
|
|
33
|
+
onExpandedChange?: (isExpanded: boolean) => void;
|
|
34
|
+
/** Words for each state. `done` is read by screen readers only; `failed` is shown. Default "Done" / "Failed". */
|
|
35
|
+
stateLabels?: Partial<Record<AssistantActivityState, string>>;
|
|
36
|
+
/** Formats `durationMs`. Defaults to `formatAssistantDuration`. */
|
|
37
|
+
formatDuration?: (ms: number) => string;
|
|
38
|
+
/** Classes on the accordion. */
|
|
39
|
+
className?: string;
|
|
40
|
+
/** Test id of the root; its parts add suffixes (see Test ids in the docs). Defaults to `assistant-activity`. */
|
|
41
|
+
'data-testid'?: string;
|
|
42
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { JSX } from 'react';
|
|
2
|
+
import { AssistantComposerProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* Where a person types to an assistant: an autosizing field, a Send button
|
|
5
|
+
* that turns into Stop while a turn runs, and optional suggestion pills that
|
|
6
|
+
* fill the field without sending.
|
|
7
|
+
*
|
|
8
|
+
* Enter sends; Shift+Enter adds a line. Enter that confirms an IME
|
|
9
|
+
* composition (Japanese, Chinese, Korean input) is left to the IME.
|
|
10
|
+
*/
|
|
11
|
+
export declare function AssistantComposer({ value, onValueChange, onSubmit, onStop, isRunning, isStopDisabled, isDisabled, label, placeholder, description, sendLabel, stopLabel, suggestions, suggestionsLabel, onSuggestionSelect, maxLength, minRows, maxRows, className, 'data-testid': dataTestId, }: AssistantComposerProps): JSX.Element;
|
|
12
|
+
export declare namespace AssistantComposer {
|
|
13
|
+
var displayName: string;
|
|
14
|
+
}
|
|
15
|
+
export default AssistantComposer;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every slot class of `AssistantComposer`. HeroUI semantic colours plus
|
|
3
|
+
* `ai-focus` for the field's focus border, so it works under `.dark`.
|
|
4
|
+
*/
|
|
5
|
+
export declare const assistantComposerClass: {
|
|
6
|
+
readonly base: "flex w-full min-w-0 flex-col gap-2";
|
|
7
|
+
readonly suggestions: "flex flex-wrap gap-2";
|
|
8
|
+
/**
|
|
9
|
+
* Pill tones are set here rather than taken from HeroUI's flat variant,
|
|
10
|
+
* whose text tone differs between HeroUI versions and read dim on a dark
|
|
11
|
+
* card in an app on HeroUI 2.7. Measured in both themes in the stories.
|
|
12
|
+
*/
|
|
13
|
+
readonly suggestion: "h-auto min-h-8 max-w-full whitespace-normal bg-default-100 py-1.5 text-left text-foreground data-[hover=true]:bg-default-200";
|
|
14
|
+
readonly row: "flex min-w-0 items-end gap-2";
|
|
15
|
+
readonly field: "min-w-0 flex-1";
|
|
16
|
+
/**
|
|
17
|
+
* The focus border is `ai-focus` with the pointer over the field too:
|
|
18
|
+
* HeroUI's bordered variant has its own focused-and-hovered rule
|
|
19
|
+
* (`border-default-foreground`, black in light and white in dark), more
|
|
20
|
+
* specific than the focus one, so it is replaced here by name.
|
|
21
|
+
*/
|
|
22
|
+
readonly inputWrapper: "border-default-200 bg-content1 data-[hover=true]:border-default-300 group-data-[focus=true]:border-ai-focus group-data-[focus=true]:data-[hover=true]:border-ai-focus";
|
|
23
|
+
readonly input: "text-sm text-foreground placeholder:text-default-500";
|
|
24
|
+
/**
|
|
25
|
+
* The hint sits under the whole row, not inside the field's column: inside
|
|
26
|
+
* it, a long hint pushed the bottom-aligned button down beside the hint.
|
|
27
|
+
* Our own element, so its tone is ours too: `default-500` clears 4.5:1 in
|
|
28
|
+
* both themes (HeroUI's description slot is `foreground-400`, 2.56:1).
|
|
29
|
+
*/
|
|
30
|
+
readonly helper: "m-0 flex w-full justify-between gap-2 text-xs text-default-500";
|
|
31
|
+
readonly count: "ms-auto flex-none tabular-nums";
|
|
32
|
+
readonly button: "flex-none";
|
|
33
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/** A ready-made question shown as a pill above the field. */
|
|
3
|
+
export interface AssistantComposerSuggestion {
|
|
4
|
+
/** Stable key; also the pill's `data-testid` suffix. */
|
|
5
|
+
key: string;
|
|
6
|
+
/** What the pill says. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** What it puts in the field. Defaults to `label`. Cut to `maxLength` when the composer has one. */
|
|
9
|
+
prompt?: string;
|
|
10
|
+
}
|
|
11
|
+
export interface AssistantComposerProps {
|
|
12
|
+
/** The field's text. Controlled: pair it with `onValueChange`. */
|
|
13
|
+
value: string;
|
|
14
|
+
/** Called with the new text on every change, and when a suggestion fills the field. */
|
|
15
|
+
onValueChange: (value: string) => void;
|
|
16
|
+
/**
|
|
17
|
+
* Enter or the send button, while not running or disabled, with the text
|
|
18
|
+
* trimmed. It does not fire when the trimmed text is empty or the value is
|
|
19
|
+
* longer than `maxLength`.
|
|
20
|
+
*/
|
|
21
|
+
onSubmit: (value: string) => void;
|
|
22
|
+
/** Shown as a Stop button in place of Send while `isRunning`. Omitted, Send is just disabled. */
|
|
23
|
+
onStop?: () => void;
|
|
24
|
+
/** A turn is running: Enter does nothing and Send becomes Stop. The field stays editable. */
|
|
25
|
+
isRunning?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Keeps Stop in place but disabled, e.g. while a stop the person asked for
|
|
28
|
+
* is being carried out. A disabled Stop fires no `onStop`.
|
|
29
|
+
*/
|
|
30
|
+
isStopDisabled?: boolean;
|
|
31
|
+
/** Disables the field, the pills and Send together. */
|
|
32
|
+
isDisabled?: boolean;
|
|
33
|
+
/** The field's accessible name. Required, and distinct from `placeholder`. */
|
|
34
|
+
label: string;
|
|
35
|
+
/** Example text inside the empty field, e.g. "Ask about this account". Not a substitute for `label`. */
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
/** A hint under the field, e.g. how Enter and Shift+Enter behave. */
|
|
38
|
+
description?: ReactNode;
|
|
39
|
+
/** Name of the send button. Defaults to "Send". */
|
|
40
|
+
sendLabel?: string;
|
|
41
|
+
/** Name of the stop button. Defaults to "Stop". */
|
|
42
|
+
stopLabel?: string;
|
|
43
|
+
/** Pills that fill the field when picked. They never send. */
|
|
44
|
+
suggestions?: AssistantComposerSuggestion[];
|
|
45
|
+
/** Name of the suggestions group. Defaults to "Suggestions". */
|
|
46
|
+
suggestionsLabel?: string;
|
|
47
|
+
/** Called after a pill has filled the field. */
|
|
48
|
+
onSuggestionSelect?: (suggestion: AssistantComposerSuggestion) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Caps the field and shows a `n/max` count under it. A longer suggestion is
|
|
51
|
+
* cut to it, and Send stays disabled while a controlled `value` is over it.
|
|
52
|
+
*/
|
|
53
|
+
maxLength?: number;
|
|
54
|
+
/** Autosize lower bound, in rows. Defaults to 2. */
|
|
55
|
+
minRows?: number;
|
|
56
|
+
/** Autosize upper bound, in rows; past it the field scrolls. Defaults to 8. */
|
|
57
|
+
maxRows?: number;
|
|
58
|
+
/** Classes on the root `<form>`. */
|
|
59
|
+
className?: string;
|
|
60
|
+
/** Test id of the root; its parts add suffixes (see Test ids in the docs). Defaults to `assistant-composer`. */
|
|
61
|
+
'data-testid'?: string;
|
|
62
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { JSX } from 'react';
|
|
2
|
+
import { AssistantMessageProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* One turn of a conversation with an assistant: the person's on the right in
|
|
5
|
+
* a neutral bubble, the assistant's on the left at full width under a small
|
|
6
|
+
* mark, with no bubble of its own. Content, copy and controls all come in as props; the pattern renders
|
|
7
|
+
* whatever `children` it is given (plain text or a rendered answer).
|
|
8
|
+
*
|
|
9
|
+
* Each turn is an `<article>` named by its author, so a screen reader can move
|
|
10
|
+
* turn by turn. It announces nothing on its own: run-state announcements
|
|
11
|
+
* belong to `AssistantNotice`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function AssistantMessage({ author, authorLabel, children, status, statusLabel, meta, actions, icon, className, classNames, 'data-testid': dataTestId, }: AssistantMessageProps): JSX.Element;
|
|
14
|
+
export declare namespace AssistantMessage {
|
|
15
|
+
var displayName: string;
|
|
16
|
+
}
|
|
17
|
+
export default AssistantMessage;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { AssistantMessageStatus } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Every slot class of `AssistantMessage`.
|
|
4
|
+
*
|
|
5
|
+
* Colours are HeroUI semantic tokens (they flip under `.dark`) plus the `ai-*`
|
|
6
|
+
* palette (which flips with them). No `bg-white`: the assistant pieces carry
|
|
7
|
+
* their own dark values because the DS has no dark theme.
|
|
8
|
+
*
|
|
9
|
+
* The assistant's turn paints nothing: it is full width with no bubble, so it
|
|
10
|
+
* shows whatever surface the app puts it on (a card, an inset box, a
|
|
11
|
+
* translucent dark panel). Painting `ai-assistant-bubble` edge to edge with no
|
|
12
|
+
* padding made a bare block on any surface but HeroUI `content1`.
|
|
13
|
+
*
|
|
14
|
+
* Secondary text is `default-500` on a plain card and `default-600` inside a
|
|
15
|
+
* fill: default-500 measures 4.40:1 on the light user bubble, under AA. See
|
|
16
|
+
* `src/tokens/__test__/assistant-surface.test.ts`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const assistantMessageClass: {
|
|
19
|
+
readonly base: "flex w-full min-w-0 flex-col gap-1.5";
|
|
20
|
+
readonly baseUser: "items-end";
|
|
21
|
+
readonly baseAssistant: "items-start";
|
|
22
|
+
readonly header: "flex min-w-0 max-w-full flex-wrap items-center gap-x-2 gap-y-0.5";
|
|
23
|
+
readonly mark: "inline-flex size-6 flex-none items-center justify-center rounded-full bg-ai-fill text-ai-accent";
|
|
24
|
+
readonly author: "min-w-0 text-sm font-semibold text-ai-accent [overflow-wrap:anywhere]";
|
|
25
|
+
readonly meta: "min-w-0 text-xs tabular-nums text-default-500 [overflow-wrap:anywhere]";
|
|
26
|
+
readonly body: "min-w-0 text-sm leading-relaxed text-foreground [overflow-wrap:anywhere]";
|
|
27
|
+
readonly bodyUser: "max-w-[85%] rounded-2xl rounded-br-md bg-ai-user-bubble px-4 py-2.5";
|
|
28
|
+
readonly bodyAssistant: "w-full";
|
|
29
|
+
/** A plain string keeps the line breaks the person typed. */
|
|
30
|
+
readonly plain: "whitespace-pre-wrap";
|
|
31
|
+
readonly status: "m-0 flex min-w-0 items-center gap-1.5 text-xs font-medium [overflow-wrap:anywhere]";
|
|
32
|
+
/**
|
|
33
|
+
* The status mark's own 14px box. Without words it is all a sighted reader
|
|
34
|
+
* sees, so it keeps its place before Iconify has loaded the icon.
|
|
35
|
+
*/
|
|
36
|
+
readonly statusIcon: "inline-flex size-3.5 flex-none";
|
|
37
|
+
readonly actions: "flex flex-wrap items-center gap-1";
|
|
38
|
+
/** The person's name, for screen readers only. */
|
|
39
|
+
readonly userLabel: "sr-only";
|
|
40
|
+
/** The default status word when `statusLabel` is empty, for screen readers only: the icon shows it. */
|
|
41
|
+
readonly statusFallback: "sr-only";
|
|
42
|
+
};
|
|
43
|
+
export declare const assistantMessageStatusClass: Record<Exclude<AssistantMessageStatus, 'complete'>, string>;
|
|
44
|
+
export declare const ASSISTANT_MESSAGE_STATUS_ICON: Record<Exclude<AssistantMessageStatus, 'complete'>, string>;
|
|
45
|
+
export declare const ASSISTANT_MESSAGE_DEFAULT_ICON = "solar:stars-minimalistic-linear";
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/** Who wrote the turn. */
|
|
3
|
+
export type AssistantMessageAuthor = 'user' | 'assistant';
|
|
4
|
+
/**
|
|
5
|
+
* How the turn ended. `stopped` means the person stopped it; `error` means
|
|
6
|
+
* it failed. Either way the content that arrived is still shown.
|
|
7
|
+
*/
|
|
8
|
+
export type AssistantMessageStatus = 'complete' | 'stopped' | 'error';
|
|
9
|
+
/** Extra classes per slot, merged after the pattern's own. */
|
|
10
|
+
export interface AssistantMessageClassNames {
|
|
11
|
+
/** The `<article>`. */
|
|
12
|
+
base?: string;
|
|
13
|
+
/** The row with the mark, the name and `meta`. */
|
|
14
|
+
header?: string;
|
|
15
|
+
/** The content. Add padding and `bg-ai-assistant-bubble` here to draw the assistant's turn as a bubble. */
|
|
16
|
+
body?: string;
|
|
17
|
+
/** The status line of a stopped or failed turn. */
|
|
18
|
+
status?: string;
|
|
19
|
+
/** The secondary line. */
|
|
20
|
+
meta?: string;
|
|
21
|
+
/** The controls under the turn. */
|
|
22
|
+
actions?: string;
|
|
23
|
+
}
|
|
24
|
+
/** A label that says something: text or an element, never empty or absent. */
|
|
25
|
+
export type AssistantMessageStatusLabel = Exclude<ReactNode, null | undefined | boolean>;
|
|
26
|
+
/**
|
|
27
|
+
* A stopped or failed turn must say so in words: without a label it would
|
|
28
|
+
* read as a finished answer, to sighted people and screen readers alike. The
|
|
29
|
+
* union makes the label required whenever `status` is not `complete`, and
|
|
30
|
+
* still accepts a `status` computed at runtime as long as a label comes with it.
|
|
31
|
+
*
|
|
32
|
+
* The type is not the only guard: an empty string, an `undefined` from an
|
|
33
|
+
* unchecked lookup, or a wrapper that loses the union all reach the component
|
|
34
|
+
* without words, so it still draws the status icon with a default word for
|
|
35
|
+
* screen readers ("Stopped" / "Failed").
|
|
36
|
+
*
|
|
37
|
+
* A wrapper keeps the requirement by omitting from the base props, not from
|
|
38
|
+
* the union: `Omit<AssistantMessageBaseProps, 'author'> & AssistantMessageStatusProps`.
|
|
39
|
+
*/
|
|
40
|
+
export type AssistantMessageStatusProps = {
|
|
41
|
+
/** Defaults to `complete`. */
|
|
42
|
+
status?: 'complete';
|
|
43
|
+
/** Ignored on a complete turn. */
|
|
44
|
+
statusLabel?: ReactNode;
|
|
45
|
+
} | {
|
|
46
|
+
/** The person stopped the turn, or it failed. The content that arrived is still shown. */
|
|
47
|
+
status: 'stopped' | 'error';
|
|
48
|
+
/** Shown under the content, e.g. "Stopped by you". Required: it is the only text that says so. */
|
|
49
|
+
statusLabel: AssistantMessageStatusLabel;
|
|
50
|
+
};
|
|
51
|
+
export interface AssistantMessageBaseProps {
|
|
52
|
+
/**
|
|
53
|
+
* Who wrote the turn. Named `author`, not `role`, because `role` on a DS
|
|
54
|
+
* pattern means the ARIA role (`FeedbackState`).
|
|
55
|
+
*/
|
|
56
|
+
author: AssistantMessageAuthor;
|
|
57
|
+
/**
|
|
58
|
+
* The turn's accessible name, e.g. "You" or "Assistant". Shown next to the
|
|
59
|
+
* assistant mark; on the person's own turn it is read by screen readers
|
|
60
|
+
* only.
|
|
61
|
+
*/
|
|
62
|
+
authorLabel: string;
|
|
63
|
+
/**
|
|
64
|
+
* The turn's content. A plain string keeps its line breaks. Empty (a turn
|
|
65
|
+
* that failed before any text arrived), no body is drawn.
|
|
66
|
+
*/
|
|
67
|
+
children?: ReactNode;
|
|
68
|
+
/** Small secondary line: a time, a duration. */
|
|
69
|
+
meta?: ReactNode;
|
|
70
|
+
/** Controls under the turn (copy, retry). Rendered as given. */
|
|
71
|
+
actions?: ReactNode;
|
|
72
|
+
/**
|
|
73
|
+
* Iconify name of the assistant mark, or `false` for none. Defaults to a
|
|
74
|
+
* sparkle. Ignored on the person's own turn.
|
|
75
|
+
*/
|
|
76
|
+
icon?: string | false;
|
|
77
|
+
/** Classes on the root `<article>`. */
|
|
78
|
+
className?: string;
|
|
79
|
+
/** Classes per slot. */
|
|
80
|
+
classNames?: AssistantMessageClassNames;
|
|
81
|
+
/** Test id of the root; its parts add suffixes (see Test ids in the docs). Defaults to `assistant-message`. */
|
|
82
|
+
'data-testid'?: string;
|
|
83
|
+
}
|
|
84
|
+
export type AssistantMessageProps = AssistantMessageBaseProps & AssistantMessageStatusProps;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { JSX } from 'react';
|
|
2
|
+
import { AssistantNoticeProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* The run state of an assistant panel: working, nothing yet, failed, a limit,
|
|
5
|
+
* offline, no access, or a plain note. A HeroUI `Alert` underneath, with one
|
|
6
|
+
* kind per state and every word supplied by the caller.
|
|
7
|
+
*
|
|
8
|
+
* Announcements follow `FeedbackState`: `error` is `role="alert"`
|
|
9
|
+
* (assertive), every other kind `role="status"` (polite). The live region
|
|
10
|
+
* wraps the title and description only; the clock, the action and the
|
|
11
|
+
* details stay outside it, so a ticking clock or revealing details is not
|
|
12
|
+
* announced.
|
|
13
|
+
*/
|
|
14
|
+
export declare function AssistantNotice({ kind, title, description, elapsed, action, details, detailsLabel, icon, role, ariaLive, className, classNames, 'data-testid': dataTestId, }: AssistantNoticeProps): JSX.Element;
|
|
15
|
+
export declare namespace AssistantNotice {
|
|
16
|
+
var displayName: string;
|
|
17
|
+
}
|
|
18
|
+
export default AssistantNotice;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AssistantNoticeKind } from './types';
|
|
2
|
+
type AlertColor = 'default' | 'danger' | 'warning';
|
|
3
|
+
interface KindStyle {
|
|
4
|
+
/** HeroUI `Alert` colour: its flat fill for the kind. */
|
|
5
|
+
color: AlertColor;
|
|
6
|
+
/** Extra base classes. `dark:` repeats are needed to beat Alert's own `dark:bg-*`. */
|
|
7
|
+
base: string;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
icon: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const assistantNoticeKindStyle: Record<AssistantNoticeKind, KindStyle>;
|
|
13
|
+
export declare const assistantNoticeClass: {
|
|
14
|
+
/** Alert centres a body with no title prop; the copy here is children, so pin it to the top-left. */
|
|
15
|
+
readonly base: "items-start gap-x-3 px-4 py-3";
|
|
16
|
+
readonly mainWrapper: "ms-0 min-h-0 min-w-0 items-start justify-start gap-0.5";
|
|
17
|
+
readonly icon: "mt-0.5 inline-flex size-5 flex-none items-center justify-center";
|
|
18
|
+
/**
|
|
19
|
+
* The live region: title and description only. The clock, the action and
|
|
20
|
+
* the details sit outside it, so a ticking clock or opening "Details" on
|
|
21
|
+
* an error does not re-announce anything.
|
|
22
|
+
*/
|
|
23
|
+
readonly live: "col-span-full row-start-1 grid min-w-0 grid-cols-subgrid gap-y-0.5";
|
|
24
|
+
/**
|
|
25
|
+
* Title, description and clock. The live region spans every column as a
|
|
26
|
+
* subgrid (title in the first, description across all), and the clock
|
|
27
|
+
* sits outside it, so a ticking clock never changes the region. Without a
|
|
28
|
+
* clock there is one column, and the title has the full width.
|
|
29
|
+
*/
|
|
30
|
+
readonly text: "grid w-full min-w-0 grid-cols-1 items-baseline gap-x-2";
|
|
31
|
+
/**
|
|
32
|
+
* With a clock, two columns: the title, then the clock on the title's
|
|
33
|
+
* baseline, in the second column of the first row. Both the region and the
|
|
34
|
+
* clock have an explicit row, or auto-placement would move the region below
|
|
35
|
+
* the clock. A title too long for its line wraps beside the clock.
|
|
36
|
+
*/
|
|
37
|
+
readonly textWithClock: "grid-cols-[auto_1fr]";
|
|
38
|
+
readonly title: "col-start-1 m-0 min-w-0 justify-self-start text-sm font-medium [overflow-wrap:anywhere]";
|
|
39
|
+
readonly elapsed: "col-start-2 row-start-1 justify-self-start whitespace-nowrap text-sm tabular-nums";
|
|
40
|
+
readonly description: "col-span-full m-0 min-w-0 text-sm [overflow-wrap:anywhere]";
|
|
41
|
+
readonly action: "mt-2 flex flex-wrap items-center gap-2";
|
|
42
|
+
readonly detailsToggle: "-ms-1.5 h-7 min-w-0 gap-1 px-1.5 text-xs font-medium text-current";
|
|
43
|
+
readonly detailsWrapper: "w-full min-w-0";
|
|
44
|
+
readonly detailsChevron: "transition-transform motion-reduce:transition-none [html[data-pos-reduce-motion=true]_&]:transition-none";
|
|
45
|
+
readonly detailsChevronOpen: "rotate-180";
|
|
46
|
+
readonly details: "mt-1 max-h-48 overflow-auto whitespace-pre-wrap rounded-md bg-content1 p-2 font-mono text-xs text-foreground [overflow-wrap:anywhere]";
|
|
47
|
+
};
|
|
48
|
+
export declare const ASSISTANT_NOTICE_DEFAULT_ROLE: Record<AssistantNoticeKind, 'alert' | 'status'>;
|
|
49
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* The run states an assistant panel shows. The caller maps its own errors to
|
|
4
|
+
* these; the pattern knows nothing about where they came from.
|
|
5
|
+
*
|
|
6
|
+
* - `thinking`: a turn is running. Pair it with `elapsed`.
|
|
7
|
+
* - `empty`: nothing asked yet.
|
|
8
|
+
* - `error`: the turn failed.
|
|
9
|
+
* - `caution`: a limit was reached (rate, daily, per-turn); no retry now.
|
|
10
|
+
* - `offline`: the service or the connection is down.
|
|
11
|
+
* - `blocked`: the person cannot use the assistant (no access, not set up).
|
|
12
|
+
* - `info`: anything else worth saying.
|
|
13
|
+
*/
|
|
14
|
+
export type AssistantNoticeKind = 'thinking' | 'empty' | 'error' | 'caution' | 'offline' | 'blocked' | 'info';
|
|
15
|
+
/** Extra classes per slot, merged after the pattern's own. */
|
|
16
|
+
export interface AssistantNoticeClassNames {
|
|
17
|
+
/** The notice. */
|
|
18
|
+
base?: string;
|
|
19
|
+
/** The title. */
|
|
20
|
+
title?: string;
|
|
21
|
+
/** The description. */
|
|
22
|
+
description?: string;
|
|
23
|
+
/** The wrapper of `action`. */
|
|
24
|
+
action?: string;
|
|
25
|
+
/** The revealed details. */
|
|
26
|
+
details?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface AssistantNoticeProps {
|
|
29
|
+
/** The run state. It picks the icon, the tone and the default announcement. */
|
|
30
|
+
kind: AssistantNoticeKind;
|
|
31
|
+
/** What is happening, in a few words, e.g. "Working…". Announced once. */
|
|
32
|
+
title: ReactNode;
|
|
33
|
+
/** More detail under the title, e.g. when they can ask again. */
|
|
34
|
+
description?: ReactNode;
|
|
35
|
+
/**
|
|
36
|
+
* A running clock next to the title ("12 s"). Hidden from screen readers so
|
|
37
|
+
* a ticking value is not announced every second; they hear the title once.
|
|
38
|
+
*/
|
|
39
|
+
elapsed?: ReactNode;
|
|
40
|
+
/** A control under the text (Retry, Sign in). Rendered as given. */
|
|
41
|
+
action?: ReactNode;
|
|
42
|
+
/** Technical detail, revealed by a "Details" toggle. */
|
|
43
|
+
details?: ReactNode;
|
|
44
|
+
/** Name of the details toggle. Defaults to "Details". */
|
|
45
|
+
detailsLabel?: string;
|
|
46
|
+
/** Iconify name to replace the kind's icon, or `false` for none. */
|
|
47
|
+
icon?: string | false;
|
|
48
|
+
/** Defaults to `alert` for `error`, `status` otherwise. */
|
|
49
|
+
role?: 'alert' | 'status';
|
|
50
|
+
/** Defaults to `assertive` for `error`, `polite` otherwise. */
|
|
51
|
+
ariaLive?: 'polite' | 'assertive' | 'off';
|
|
52
|
+
/** Classes on the notice. */
|
|
53
|
+
className?: string;
|
|
54
|
+
/** Classes per slot. */
|
|
55
|
+
classNames?: AssistantNoticeClassNames;
|
|
56
|
+
/** Test id of the root; its parts add suffixes (see Test ids in the docs). Defaults to `assistant-notice`. */
|
|
57
|
+
'data-testid'?: string;
|
|
58
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { JSX } from 'react';
|
|
2
|
+
import { ScrollToLatestProps } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* A scroll box that stays on its newest content, like a chat transcript. It
|
|
5
|
+
* follows the bottom until the person scrolls up, then offers a "Jump to
|
|
6
|
+
* newest" button; reaching the bottom again, by scrolling or the button,
|
|
7
|
+
* resumes following. Built on HeroUI's `ScrollShadow`.
|
|
8
|
+
*
|
|
9
|
+
* Only scrolling UP stops the following, so the box's own scrolling (and the
|
|
10
|
+
* browser clamping after content shrinks) never does. Being at the bottom
|
|
11
|
+
* resumes it, however the box got there: a scroll, or content that now fits
|
|
12
|
+
* without the box moving (which fires no scroll event).
|
|
13
|
+
*/
|
|
14
|
+
export declare function ScrollToLatest({ children, label, followKey, jumpLabel, onFollowChange, threshold, className, classNames, 'data-testid': dataTestId, }: ScrollToLatestProps): JSX.Element;
|
|
15
|
+
export declare namespace ScrollToLatest {
|
|
16
|
+
var displayName: string;
|
|
17
|
+
}
|
|
18
|
+
export default ScrollToLatest;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every slot class of `ScrollToLatest`. The box takes its height from its
|
|
3
|
+
* parent (`h-full` / `flex-1`), so give the parent one.
|
|
4
|
+
*/
|
|
5
|
+
export declare const scrollToLatestClass: {
|
|
6
|
+
readonly base: "relative flex min-h-0 w-full flex-col";
|
|
7
|
+
readonly scroller: "min-h-0 flex-1 overflow-y-auto overscroll-contain rounded-[inherit] outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-ai-focus";
|
|
8
|
+
readonly content: "min-w-0";
|
|
9
|
+
readonly jumpBar: "pointer-events-none absolute inset-x-0 bottom-3 flex justify-center";
|
|
10
|
+
readonly jump: "pointer-events-auto border border-divider bg-content1 text-foreground shadow-medium";
|
|
11
|
+
};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
/** Extra classes per slot, merged after the pattern's own. */
|
|
3
|
+
export interface ScrollToLatestClassNames {
|
|
4
|
+
/** The root. */
|
|
5
|
+
base?: string;
|
|
6
|
+
/** The scrolling region (HeroUI `ScrollShadow`). */
|
|
7
|
+
scroller?: string;
|
|
8
|
+
/** The wrapper around `children` that the box watches for growth. */
|
|
9
|
+
content?: string;
|
|
10
|
+
/** The "Jump to newest" button. */
|
|
11
|
+
jump?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ScrollToLatestProps {
|
|
14
|
+
/** The transcript, usually one `AssistantMessage` per turn. */
|
|
15
|
+
children: ReactNode;
|
|
16
|
+
/** Accessible name of the scrolling region, e.g. "Conversation". */
|
|
17
|
+
label: string;
|
|
18
|
+
/**
|
|
19
|
+
* Change it when new content arrives (a message count, the last id). While
|
|
20
|
+
* the person is following the bottom, the box scrolls to it. Growth inside
|
|
21
|
+
* the content (a group expanding) is followed without it.
|
|
22
|
+
*/
|
|
23
|
+
followKey?: unknown;
|
|
24
|
+
/** Text of the button that returns to the bottom. Defaults to "Jump to newest". */
|
|
25
|
+
jumpLabel?: string;
|
|
26
|
+
/** Called when the person stops or starts following the bottom. */
|
|
27
|
+
onFollowChange?: (isFollowing: boolean) => void;
|
|
28
|
+
/** How close to the bottom, in px, still counts as "at the bottom". Defaults to 32. */
|
|
29
|
+
threshold?: number;
|
|
30
|
+
/** Classes on the root. Give it a height here or on its parent. */
|
|
31
|
+
className?: string;
|
|
32
|
+
/** Classes per slot. */
|
|
33
|
+
classNames?: ScrollToLatestClassNames;
|
|
34
|
+
/** Test id of the root; its parts add suffixes (see Test ids in the docs). Defaults to `scroll-to-latest`. */
|
|
35
|
+
'data-testid'?: string;
|
|
36
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The opaque colour actually painted behind `element`: its own background and
|
|
3
|
+
* every ancestor's, composited from the nearest opaque one down, over white.
|
|
4
|
+
*
|
|
5
|
+
* Contrast has to be measured against this, not against the element's own
|
|
6
|
+
* `backgroundColor`: HeroUI's flat fills are translucent in dark mode
|
|
7
|
+
* (`dark:bg-danger-50/50`), so the computed value alone reads as a colour
|
|
8
|
+
* nobody sees.
|
|
9
|
+
*/
|
|
10
|
+
export declare function backdropColor(element: Element): string;
|
|
11
|
+
/** `#1d4ed8` → `rgb(29, 78, 216)`, the form `getComputedStyle` reports. */
|
|
12
|
+
export declare function rgbOf(hex: string): string;
|