@sapia-ai/chatbot-v3 1.0.2 → 1.1.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 (49) hide show
  1. package/dist/{EmbeddedVideoPlayer-CNcds-dW.js → EmbeddedVideoPlayer-COUZHpPa.js} +14 -14
  2. package/dist/{Preview-Dmns6UWL.js → Preview-wPnYtccF.js} +3 -3
  3. package/dist/api/client.d.ts +6 -2
  4. package/dist/api/types/messages.d.ts +16 -5
  5. package/dist/api/types/responses.d.ts +48 -7
  6. package/dist/api.js +2 -2
  7. package/dist/core/bubbles/types.d.ts +2 -5
  8. package/dist/core/i18n/languages.d.ts +6 -1
  9. package/dist/core/interview.d.ts +15 -8
  10. package/dist/core/media/mimeTypes.d.ts +6 -1
  11. package/dist/core/media/recorder.d.ts +11 -2
  12. package/dist/core/store.d.ts +21 -1
  13. package/dist/core/theme/contrast.d.ts +6 -0
  14. package/dist/core/theme/cssVariables.d.ts +5 -0
  15. package/dist/core/theme/types.d.ts +3 -0
  16. package/dist/{dist-BSi28_cP.js → dist-BOe6WyvN.js} +92 -92
  17. package/dist/{dist-B9SbMqZU.js → dist-Bwlks_W8.js} +98 -98
  18. package/dist/{fix-webm-duration-BWL-ZRDX.js → fix-webm-duration-QAy9T6AR.js} +2 -2
  19. package/dist/index.js +7443 -5031
  20. package/dist/{jsx-runtime-8QqjHL5t.js → jsx-runtime-24G8w2Bf.js} +2 -2
  21. package/dist/{messages-Czlp86gl.js → messages-DCvBcspv.js} +260 -245
  22. package/dist/react/AnswerInput.d.ts +4 -5
  23. package/dist/react/bubbleViewModel.d.ts +25 -9
  24. package/dist/react/strings.d.ts +26 -3
  25. package/dist/{react-DJysvNxz.js → react-BjO903pN.js} +33 -33
  26. package/dist/{react-QrtEtv1G.js → react-Bw5AugJc.js} +31 -31
  27. package/dist/{react-dE7BdGuR.js → react-D8hV9uow.js} +10 -10
  28. package/dist/{react-NauLB_WU.js → react-DDjfkpOE.js} +38 -38
  29. package/dist/{react-Dbs-sPBo.js → react-DEF8h8hc.js} +37 -37
  30. package/dist/{react-B0K-sClg.js → react-DOgLiyZi.js} +2 -2
  31. package/dist/{react-be7q6yw_.js → react-DPFikgTM.js} +55 -55
  32. package/dist/{react-ChCcxVnw.js → react-qa12DkXH.js} +34 -34
  33. package/dist/{react-czRIxT0g.js → react-y4vaW3OH.js} +46 -46
  34. package/dist/ui/chat/ChatBubble/ChatBubble.d.ts +14 -5
  35. package/dist/ui/chat/ChatLog/ChatLog.d.ts +6 -4
  36. package/dist/ui/chat/EditBanner/EditBanner.d.ts +16 -0
  37. package/dist/ui/chat/SummaryCard/SummaryCard.d.ts +46 -0
  38. package/dist/ui/chat/types.d.ts +21 -8
  39. package/dist/ui/constants.d.ts +1 -0
  40. package/dist/ui/inputs/OptionList/OptionList.d.ts +3 -1
  41. package/dist/ui/inputs/TextAnswerInput/TextAnswerInput.d.ts +11 -4
  42. package/dist/ui/inputs/VideoRecorder/VideoRecorder.d.ts +6 -2
  43. package/dist/ui/markdown.d.ts +4 -0
  44. package/dist/ui/motion.d.ts +2 -0
  45. package/dist/ui/primitives/Button/Button.d.ts +5 -1
  46. package/dist/ui/primitives/Dialog/Dialog.d.ts +2 -1
  47. package/dist/ui/shell/WidgetRoot/WidgetRoot.d.ts +7 -1
  48. package/dist-umd/sapia-chatbot.umd.js +394 -270
  49. package/package.json +4 -1
@@ -22,9 +22,14 @@ export type ChatBubbleLabels = {
22
22
  retry: string;
23
23
  optionsGroup: string;
24
24
  edit: string;
25
- cancelEdit: string;
26
- /** Eyebrow on every agent transcript turn, e.g. "Interviewer". */
27
- speakerLabel: string;
25
+ /** Badge on the answer currently being re-edited, e.g. "Editing". */
26
+ editing: string;
27
+ /** Visually-hidden bubble prefix, e.g. "Interviewer" — sender is otherwise visual only. */
28
+ senderAgent: string;
29
+ /** Visually-hidden bubble prefix, e.g. "You". */
30
+ senderCandidate: string;
31
+ /** Appended inside markdown links that open a new tab. */
32
+ opensNewTab: string;
28
33
  };
29
34
  export type ChatBubbleProps = {
30
35
  item: ChatLogItem;
@@ -36,7 +41,6 @@ export type ChatBubbleProps = {
36
41
  onSelectOption?: (optionId: string) => void;
37
42
  onSubmitSlider?: (score: number) => void;
38
43
  onEdit?: (questionId: string) => void;
39
- onCancelEdit?: () => void;
40
44
  onRetryVideo?: () => void;
41
45
  onResolveVideoUrl?: (questionId: string, extension: string) => Promise<string>;
42
46
  /** Attaches to the bubble's root <li> so the log can scroll it into view. */
@@ -44,4 +48,9 @@ export type ChatBubbleProps = {
44
48
  /** Rendered inside the question card body (e.g. the video recorder). */
45
49
  footer?: ReactNode;
46
50
  };
47
- export declare const ChatBubble: ({ item, labels, sliderLabels, dropdownLabels, mediaLabels, onRetry, onSelectOption, onSubmitSlider, onEdit, onCancelEdit, onRetryVideo, onResolveVideoUrl, rootRef, footer, }: ChatBubbleProps) => import("react").JSX.Element;
51
+ /**
52
+ * Memoized: the transcript re-renders on every store write (roughly 60/s while an agent message
53
+ * streams in), and each render re-parses every settled paragraph's markdown from scratch. Default
54
+ * shallow comparison suffices — ChatLog is passed stable item and label references.
55
+ */
56
+ export declare const ChatBubble: import('react').MemoExoticComponent<({ item, labels, sliderLabels, dropdownLabels, mediaLabels, onRetry, onSelectOption, onSubmitSlider, onEdit, onRetryVideo, onResolveVideoUrl, rootRef, footer, }: ChatBubbleProps) => import("react").JSX.Element>;
@@ -18,16 +18,18 @@ export type ChatLogProps = {
18
18
  onSelectOption?: (optionId: string) => void;
19
19
  onSubmitSlider?: (score: number) => void;
20
20
  onEdit?: (questionId: string) => void;
21
- onCancelEdit?: () => void;
22
21
  onRetryVideo?: () => void;
23
22
  onResolveVideoUrl?: (questionId: string, extension: string) => Promise<string>;
24
23
  /** Node rendered inside the question card identified by cardFooterItemId (e.g. video recorder). */
25
24
  cardFooter?: ReactNode;
26
25
  /** Bubble id whose card receives cardFooter. */
27
26
  cardFooterItemId?: string;
27
+ /** Node pinned as the first transcript entry (e.g. the pre-interview summary card). */
28
+ header?: ReactNode;
28
29
  };
29
30
  /**
30
- * Scrollable interview transcript. aria-live announces new bubbles to screen
31
- * readers; the log auto-scrolls to the newest message.
31
+ * Scrollable interview transcript. The log auto-scrolls to the newest message; settled turns
32
+ * are announced once through a separate visually-hidden live region (the transcript itself is
33
+ * deliberately not one — see the announcer effect below).
32
34
  */
33
- export declare const ChatLog: ({ items, typing, typingLabel, typingReadingLabel, typingThinkingLabel, bubbleLabels, sliderLabels, dropdownLabels, mediaLabels, onRetry, onSelectOption, onSubmitSlider, onEdit, onCancelEdit, onRetryVideo, onResolveVideoUrl, cardFooter, cardFooterItemId, }: ChatLogProps) => import("react").JSX.Element;
35
+ export declare const ChatLog: ({ items, typing, typingLabel, typingReadingLabel, typingThinkingLabel, bubbleLabels, sliderLabels, dropdownLabels, mediaLabels, onRetry, onSelectOption, onSubmitSlider, onEdit, onRetryVideo, onResolveVideoUrl, cardFooter, cardFooterItemId, header, }: ChatLogProps) => import("react").JSX.Element;
@@ -0,0 +1,16 @@
1
+ export type EditBannerProps = {
2
+ /** e.g. "Editing your answer". */
3
+ label: string;
4
+ /** Card label of the question under edit, e.g. "Question 3 of 11". */
5
+ questionLabel?: string;
6
+ /** Cancel copy, server-supplied ("Press ESC or click here to cancel editing"). */
7
+ cancelLabel: string;
8
+ onCancel: () => void;
9
+ };
10
+ /**
11
+ * Docked strip naming the answer under edit and offering the single cancel control.
12
+ * Repeats the composer's own column classes so it lines up with the input box at any
13
+ * container width. role="status" so entering edit mode is announced; the Escape
14
+ * shortcut it advertises is bound in react/SapiaChatbot.
15
+ */
16
+ export declare const EditBanner: ({ label, questionLabel, cancelLabel, onCancel }: EditBannerProps) => import("react").JSX.Element;
@@ -0,0 +1,46 @@
1
+ /** Question-type family that selects a row icon. */
2
+ export declare enum SummaryKind {
3
+ TEXT = "text",
4
+ VIDEO = "video",
5
+ VOICE = "voice",
6
+ CHOICE = "choice",
7
+ SELECT = "select",
8
+ RATING = "rating",
9
+ GENERIC = "generic"
10
+ }
11
+ /** One breakdown row: a count with its FE-owned label and (optional) description. */
12
+ export type SummaryCategory = {
13
+ count: number;
14
+ label: string;
15
+ description?: string;
16
+ /** Drives the row icon; falls back to a generic mark when omitted. */
17
+ kind?: SummaryKind;
18
+ };
19
+ export type SummaryCardProps = {
20
+ /** Backend-authored heading, e.g. "Welcome to your interview — here's what to expect.". */
21
+ title: string;
22
+ /** Uppercase label for the total panel, e.g. "Total". */
23
+ totalLabel: string;
24
+ /** Total-questions meta, e.g. "3 questions". */
25
+ questionsLabel: string;
26
+ /** Estimated-time meta, e.g. "~9 min". */
27
+ minutesLabel: string;
28
+ /** Per-category breakdown rows (already mapped to FE labels). */
29
+ categories: SummaryCategory[];
30
+ /** BE-authored Continue label; rendered as a button only when onContinue is set (gate mode). */
31
+ continueLabel?: string;
32
+ /** Continue handler; its presence switches the card into blocking gate mode. */
33
+ onContinue?: () => void;
34
+ /** Play the pre-interview reveal (typing dots → title types in → card fades in). */
35
+ animate?: boolean;
36
+ /** Accessible label for the reveal's typing-dots phase. */
37
+ typingLabel?: string;
38
+ /** Phrase shown beside the dots during the reveal ("Thinking…"). */
39
+ thinkingLabel?: string;
40
+ };
41
+ /**
42
+ * Pre-interview summary. An elevated white card with the agent's welcome title,
43
+ * a centred TOTAL panel (question count + estimated time), and an accent-barred
44
+ * breakdown list — one icon row per question type.
45
+ */
46
+ export declare const SummaryCard: ({ title, totalLabel, questionsLabel, minutesLabel, categories, continueLabel, onContinue, animate, typingLabel, thinkingLabel, }: SummaryCardProps) => import("react").JSX.Element;
@@ -1,4 +1,21 @@
1
- export type ChatItemSender = 'agent' | 'candidate';
1
+ export declare enum ChatItemSender {
2
+ AGENT = "agent",
3
+ CANDIDATE = "candidate"
4
+ }
5
+ /** Visual treatment of a bubble: plain conversational, or the elevated question card. */
6
+ export declare enum ChatBubbleVariant {
7
+ BUBBLE = "bubble",
8
+ CARD = "card"
9
+ }
10
+ /**
11
+ * How options render: a numbered button list (MCQ/BGQ), a searchable dropdown, or a
12
+ * single full-width CTA pill for one-off control prompts (consent / submit).
13
+ */
14
+ export declare enum ChatOptionsKind {
15
+ LIST = "list",
16
+ DROPDOWN = "dropdown",
17
+ ACTION = "action"
18
+ }
2
19
  /** A selectable answer option (MCQ/dropdown option or button-group button). */
3
20
  export type ChatOptionItem = {
4
21
  id: string;
@@ -36,18 +53,14 @@ export type ChatLogItem = {
36
53
  sender: ChatItemSender;
37
54
  /** Message paragraphs in display order. */
38
55
  paragraphs: string[];
39
- /** Visual treatment: plain conversational bubble, or the elevated question card. */
40
- variant?: 'bubble' | 'card';
41
- /** Card-only eyebrow, e.g. "Question 2 of 4" or "Single choice question". */
56
+ variant?: ChatBubbleVariant;
57
+ /** Card-only label, e.g. "Question 2 of 4". */
42
58
  questionLabel?: string;
43
59
  /** Card-only dark badge, e.g. "Video". Present only on video question cards. */
44
60
  questionBadge?: string;
45
- /** Sanitized rich-text HTML for a candidate answer; rendered instead of plain paragraphs. */
46
- answerHtml?: string;
47
61
  /** Selectable options rendered inside the bubble (option-type questions). */
48
62
  options?: ChatOptionItem[];
49
- /** How the options render: a button list (MCQ/BGQ) or a searchable dropdown. */
50
- optionsKind?: 'list' | 'dropdown';
63
+ optionsKind?: ChatOptionsKind;
51
64
  /** The chosen option once answered (or while submitting). */
52
65
  selectedOptionId?: string;
53
66
  /** Options accept clicks (current unanswered question only). */
@@ -1,2 +1,3 @@
1
1
  /** Class scoping every style in dist/sapia-chatbot.css (see src/styles/index.css). */
2
2
  export declare const WIDGET_ROOT_CLASS = "sapia-chatbot";
3
+ export declare const SLIM_SCROLLBAR = "sc:[scrollbar-width:thin] sc:[scrollbar-color:#8f8f96_transparent] sc:[&::-webkit-scrollbar]:w-1.5 sc:[&::-webkit-scrollbar-track]:bg-transparent sc:[&::-webkit-scrollbar-thumb]:rounded-full sc:[&::-webkit-scrollbar-thumb]:bg-field-border";
@@ -7,6 +7,8 @@ export type OptionListProps = {
7
7
  enabled?: boolean;
8
8
  /** Submission in flight for the selected option. */
9
9
  pending?: boolean;
10
+ /** Caps the height and scrolls the options inside the bordered box (long dropdown lists). */
11
+ scrollable?: boolean;
10
12
  groupLabel: string;
11
13
  onSelect?: (optionId: string) => void;
12
14
  };
@@ -15,4 +17,4 @@ export type OptionListProps = {
15
17
  * Pure presentational: single select, real buttons, keyboard accessible.
16
18
  * Replaces legacy components/OptionList (AntD-free).
17
19
  */
18
- export declare const OptionList: ({ options, selectedOptionId, enabled, pending, groupLabel, onSelect, }: OptionListProps) => import("react").JSX.Element;
20
+ export declare const OptionList: ({ options, selectedOptionId, enabled, pending, scrollable, groupLabel, onSelect, }: OptionListProps) => import("react").JSX.Element;
@@ -2,13 +2,12 @@ export type RichToolbarLabels = {
2
2
  toolbar: string;
3
3
  bold: string;
4
4
  italic: string;
5
- underline: string;
6
5
  bulletList: string;
7
6
  numberList: string;
8
7
  };
9
8
  export type TextAnswerInputProps = {
10
- /** Receives plain text and, in rich mode, the sanitized HTML rendering. */
11
- onSubmit: (text: string, html?: string) => void;
9
+ /** Receives the answer text — markdown in rich mode, plain text otherwise. */
10
+ onSubmit: (text: string) => void;
12
11
  disabled?: boolean;
13
12
  /** Character rules from the current question (legacy RuleName min/max/recommended). */
14
13
  minLength?: number;
@@ -28,5 +27,13 @@ export type TextAnswerInputProps = {
28
27
  toolbarLabels?: RichToolbarLabels;
29
28
  /** Called when the candidate pastes (AGC paste logging). */
30
29
  onPaste?: () => void;
30
+ /** SC 1.3.5 token for PII questions (e.g. "email", "name"); omitted for free-text answers. */
31
+ autoComplete?: string;
32
+ /** Plain text the composer opens on (re-editing a submitted answer). */
33
+ initialText?: string;
34
+ /** Markdown the composer opens on in rich mode; rendered to HTML for the editor. */
35
+ initialMarkdown?: string;
36
+ /** Moves focus to the editor on mount (used when an edit opens the composer). */
37
+ focusOnMount?: boolean;
31
38
  };
32
- export declare const TextAnswerInput: ({ onSubmit, disabled, minLength, maxLength, recommendedLength, placeholder, sendLabel, minLengthHint, okHint, footer, inputLabel, richText, toolbarLabels, onPaste, }: TextAnswerInputProps) => import("react").JSX.Element;
39
+ export declare const TextAnswerInput: ({ onSubmit, disabled, minLength, maxLength, recommendedLength, placeholder, sendLabel, minLengthHint, okHint, footer, inputLabel, richText, toolbarLabels, onPaste, autoComplete, initialText, initialMarkdown, focusOnMount, }: TextAnswerInputProps) => import("react").JSX.Element;
@@ -1,4 +1,5 @@
1
1
  import { RefObject } from 'react';
2
+ import { RecorderPhase } from '../../../core/media/recorder';
2
3
  export type VideoRecorderLabels = {
3
4
  openCamera: string;
4
5
  startRecording: string;
@@ -14,10 +15,13 @@ export type VideoRecorderLabels = {
14
15
  off?: string;
15
16
  finishHint?: string;
16
17
  cameraOff?: string;
18
+ /** Discrete recording status; the per-second timer stays aria-live="off". */
19
+ recordingStarted?: string;
20
+ recordingStopped?: string;
21
+ secondsRemaining?: string;
17
22
  };
18
- export type VideoRecorderPhase = 'idle' | 'preview' | 'recording' | 'review' | 'error';
19
23
  export type VideoRecorderProps = {
20
- phase: VideoRecorderPhase;
24
+ phase: RecorderPhase;
21
25
  elapsedSeconds: number;
22
26
  maxSeconds: number;
23
27
  /** True once a recording is captured and ready to submit. */
@@ -0,0 +1,4 @@
1
+ /** Editor HTML to the markdown stored on the answer. */
2
+ export declare const htmlToMarkdown: (html: string) => string;
3
+ /** Stored markdown back to HTML for the contentEditable editor; sanitized before it is injected. */
4
+ export declare const markdownToHtml: (markdown: string) => string;
@@ -0,0 +1,2 @@
1
+ /** True when the OS requests reduced motion. False where matchMedia is unavailable (SSR/jsdom). */
2
+ export declare const prefersReducedMotion: () => boolean;
@@ -1,6 +1,10 @@
1
1
  import { ButtonHTMLAttributes, ReactNode } from 'react';
2
+ export declare enum ButtonVariant {
3
+ PRIMARY = "primary",
4
+ GHOST = "ghost"
5
+ }
2
6
  export type ButtonProps = {
3
7
  children: ReactNode;
4
- variant?: 'primary' | 'ghost';
8
+ variant?: ButtonVariant;
5
9
  } & ButtonHTMLAttributes<HTMLButtonElement>;
6
10
  export declare const Button: ({ children, variant, type, className, ...rest }: ButtonProps) => import("react").JSX.Element;
@@ -1,8 +1,9 @@
1
1
  import { ReactNode } from 'react';
2
+ import { ButtonVariant } from '../Button/Button';
2
3
  export type DialogAction = {
3
4
  label: string;
4
5
  onClick: () => void;
5
- variant?: 'primary' | 'ghost';
6
+ variant?: ButtonVariant;
6
7
  };
7
8
  export type DialogProps = {
8
9
  open: boolean;
@@ -4,9 +4,15 @@ export type WidgetRootProps = {
4
4
  /** CSS custom properties (--sc-*) produced by core/theme. */
5
5
  themeVariables?: Record<string, string>;
6
6
  dir?: 'ltr' | 'rtl';
7
+ /** Interview language (SC 3.1.2) — may differ from the host page's html lang. */
8
+ lang?: string;
9
+ /** Accessible name for the widget region, so host-page users can navigate to it. */
10
+ label?: string;
7
11
  };
8
12
  /**
9
13
  * Root element of the widget. Carries the scoping class for all packaged
10
14
  * styles plus the theme CSS variables, so theming never re-renders children.
15
+ * Exposed as a labelled region: the widget is a guest on a host page and must be
16
+ * reachable, without claiming a top-level landmark the host already owns.
11
17
  */
12
- export declare const WidgetRoot: ({ children, themeVariables, dir }: WidgetRootProps) => import("react").JSX.Element;
18
+ export declare const WidgetRoot: ({ children, themeVariables, dir, lang, label, }: WidgetRootProps) => import("react").JSX.Element;