@sapia-ai/chatbot-v3 1.1.0 → 1.2.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 (52) hide show
  1. package/dist/{EmbeddedVideoPlayer-COUZHpPa.js → EmbeddedVideoPlayer-C_8tKis8.js} +176 -186
  2. package/dist/{Preview-wPnYtccF.js → Preview-CbNUoib5.js} +3 -3
  3. package/dist/api/types/messages.d.ts +10 -2
  4. package/dist/api/types/responses.d.ts +1 -0
  5. package/dist/api.js +2 -2
  6. package/dist/core/bubbles/convert.d.ts +10 -0
  7. package/dist/core/bubbles/types.d.ts +1 -1
  8. package/dist/core/constants.d.ts +18 -0
  9. package/dist/core/events.d.ts +7 -0
  10. package/dist/core/internalUser.d.ts +13 -0
  11. package/dist/core/interview.d.ts +6 -2
  12. package/dist/core/persistence.d.ts +3 -0
  13. package/dist/core/redirect.d.ts +2 -0
  14. package/dist/core/store.d.ts +15 -1
  15. package/dist/core/validation/wordCount.d.ts +1 -0
  16. package/dist/{dist-Bwlks_W8.js → dist-7zan-9V3.js} +3 -3
  17. package/dist/embed/compat.d.ts +1 -1
  18. package/dist/embed/createChatbot.d.ts +4 -0
  19. package/dist/{fix-webm-duration-QAy9T6AR.js → fix-webm-duration-CJ-IsmuV.js} +2 -2
  20. package/dist/index.d.ts +1 -0
  21. package/dist/index.js +6055 -5621
  22. package/dist/{jsx-runtime-24G8w2Bf.js → jsx-runtime-CpK9sRtl.js} +2 -2
  23. package/dist/{messages-DCvBcspv.js → messages-t5h3u6nr.js} +5 -3
  24. package/dist/react/AnswerInput.d.ts +11 -5
  25. package/dist/react/SapiaChatbot.d.ts +5 -1
  26. package/dist/react/bubbleViewModel.d.ts +29 -6
  27. package/dist/react/strings.d.ts +29 -3
  28. package/dist/{react-DEF8h8hc.js → react--hlAJW6n.js} +3 -3
  29. package/dist/{react-y4vaW3OH.js → react-B6p_EGvt.js} +3 -3
  30. package/dist/{react-DDjfkpOE.js → react-BKQw6USG.js} +3 -3
  31. package/dist/{react-qa12DkXH.js → react-Bh9U4IBe.js} +3 -3
  32. package/dist/{react-DOgLiyZi.js → react-BtFG4mMU.js} +2 -2
  33. package/dist/{react-Bw5AugJc.js → react-Bx1VLQqP.js} +3 -3
  34. package/dist/{react-DPFikgTM.js → react-DFkljL-6.js} +3 -3
  35. package/dist/{react-BjO903pN.js → react-DJK2_GuR.js} +3 -3
  36. package/dist/{react-D8hV9uow.js → react-yXcg2glq.js} +3 -3
  37. package/dist/ui/chat/ChatBubble/ChatBubble.d.ts +1 -1
  38. package/dist/ui/chat/ChatLog/ChatLog.d.ts +4 -1
  39. package/dist/ui/chat/HintDisclosure/HintDisclosure.d.ts +20 -0
  40. package/dist/ui/chat/types.d.ts +5 -0
  41. package/dist/ui/constants.d.ts +5 -0
  42. package/dist/ui/hooks/usePreventPaste.d.ts +17 -0
  43. package/dist/ui/inputs/SliderInput/SliderInput.d.ts +1 -2
  44. package/dist/ui/inputs/TextAnswerInput/TextAnswerInput.d.ts +28 -11
  45. package/dist/ui/media/EmbeddedVideo/EmbeddedVideoPlayer.d.ts +1 -1
  46. package/dist/ui/primitives/MarkdownText/MarkdownText.d.ts +24 -0
  47. package/dist/ui/shell/CohortClosedScreen/CohortClosedScreen.d.ts +9 -0
  48. package/dist/ui/shell/DoneScreen/DoneScreen.d.ts +4 -1
  49. package/dist/ui/shell/Header/Header.d.ts +9 -0
  50. package/dist/ui/shell/TerminalMessageScreen/TerminalMessageScreen.d.ts +10 -0
  51. package/dist-umd/sapia-chatbot.umd.js +305 -305
  52. package/package.json +1 -1
@@ -1,4 +1,4 @@
1
- import { S as e } from "./messages-DCvBcspv.js";
1
+ import { C as e } from "./messages-t5h3u6nr.js";
2
2
  //#region ../../node_modules/.pnpm/react@19.2.7/node_modules/react/cjs/react-jsx-runtime.production.js
3
3
  var t = /* @__PURE__ */ e(((e) => {
4
4
  var t = Symbol.for("react.transitional.element"), n = Symbol.for("react.fragment");
@@ -21,4 +21,4 @@ var t = /* @__PURE__ */ e(((e) => {
21
21
  //#endregion
22
22
  export { n as t };
23
23
 
24
- //# sourceMappingURL=jsx-runtime-24G8w2Bf.js.map
24
+ //# sourceMappingURL=jsx-runtime-CpK9sRtl.js.map
@@ -1968,11 +1968,13 @@ var $ = /* @__PURE__ */ function(e) {
1968
1968
  }({}), Xn = /* @__PURE__ */ function(e) {
1969
1969
  return e.FI_ATS_ASSESSMENT_ORDER_COMPLETED = "FI_ATS_ASSESSMENT_ORDER_COMPLETED", e.FI_ASSESSMENT_STARTED = "FI_ASSESSMENT_STARTED", e.FI_ASSESSMENT_PII_SAVED = "FI_ASSESSMENT_PII_SAVED", e.FI_ASSESSMENT_PII_COMPLETED = "FI_ASSESSMENT_PII_COMPLETED", e.FI_ASSESSMENT_INPROGRESS = "FI_ASSESSMENT_INPROGRESS", e.FI_ASSESSMENT_COMPLETED = "FI_ASSESSMENT_COMPLETED", e.FI_ASSESSMENT_ENDED = "FI_ASSESSMENT_ENDED", e.FI_ASSESSMENT_DROPPED_OUT = "FI_ASSESSMENT_DROPPED_OUT", e.FI_PREDICTION_STARTED = "FI_PREDICTION_STARTED", e.FI_PREDICTION_COMPLETED = "FI_PREDICTION_COMPLETED", e.FI_ASSESSMENT_DUPLICATED = "FI_ASSESSMENT_DUPLICATED", e;
1970
1970
  }({}), Zn = /* @__PURE__ */ function(e) {
1971
- return e.TEXT = "Text", e.VOICE_PLAYER = "VoicePlayer", e.VIDEO_PLAYER = "VideoPlayer", e.SLIDER = "Slider", e;
1971
+ return e.SUBMIT_QUESTION = "SUBMIT_QUESTION_MESSAGE", e.FEEDBACK_CUSTOMER_SATISFACTION_QUESTION = "FEEDBACK_CUSTOMER_SATISFACTION_QUESTION_MESSAGE", e;
1972
1972
  }({}), Qn = /* @__PURE__ */ function(e) {
1973
+ return e.TEXT = "Text", e.VOICE_PLAYER = "VoicePlayer", e.VIDEO_PLAYER = "VideoPlayer", e.SLIDER = "Slider", e;
1974
+ }({}), $n = /* @__PURE__ */ function(e) {
1973
1975
  return e.HINT = "Hint", e.QUESTION = "Question", e;
1974
1976
  }({});
1975
1977
  //#endregion
1976
- export { s as C, o as S, Mn as _, Qn as a, Pn as b, qn as c, Jn as d, Bn as f, An as g, jn as h, Gn as i, Wn as l, Ln as m, Yn as n, Un as o, $ as p, Zn as r, Kn as s, Xn as t, Hn as u, On as v, l as w, Nn as x, kn as y };
1978
+ export { o as C, Nn as S, l as T, An as _, Zn as a, kn as b, Kn as c, Hn as d, Jn as f, jn as g, Ln as h, Gn as i, qn as l, $ as m, Yn as n, $n as o, Bn as p, Qn as r, Un as s, Xn as t, Wn as u, Mn as v, s as w, Pn as x, On as y };
1977
1979
 
1978
- //# sourceMappingURL=messages-DCvBcspv.js.map
1980
+ //# sourceMappingURL=messages-t5h3u6nr.js.map
@@ -1,12 +1,14 @@
1
1
  import { InterviewEngine } from '../core/interview';
2
2
  import { Region } from '../api/config';
3
- import { CurrentInputKind, EditingDraft, TextRules } from './bubbleViewModel';
3
+ import { CurrentInputKind, EditingDraft, WordCountRule } from './bubbleViewModel';
4
4
  import { UiStrings } from './strings';
5
5
  export type AnswerInputProps = {
6
6
  engine: InterviewEngine;
7
+ /** Scopes the "don't show me this again" opt-out to this interview. */
8
+ assessmentId: string;
7
9
  kind: CurrentInputKind;
8
10
  disabled: boolean;
9
- textRules: TextRules;
11
+ wordCountRule: WordCountRule;
10
12
  strings: UiStrings;
11
13
  candidateCountryCode?: string;
12
14
  region: Region;
@@ -14,7 +16,11 @@ export type AnswerInputProps = {
14
16
  maxVideoSeconds?: number;
15
17
  /** Stored answer to open the composer on when re-editing. */
16
18
  draft?: EditingDraft;
17
- /** Called when the candidate pastes into a text answer (AGC paste log). */
18
- onPaste?: () => void;
19
+ /** Permits paste in the free-text composer (ALLOW_PASTE tag or an internal session). */
20
+ allowPaste: boolean;
21
+ /** The optional post-interview feedback comment: no word rules, and an empty send is a skip. */
22
+ isFeedbackComment: boolean;
23
+ /** Called for each blocked paste attempt (AGC paste log). */
24
+ onBlockedPaste?: () => void;
19
25
  };
20
- export declare const AnswerInput: ({ engine, kind, disabled, textRules, strings, candidateCountryCode, region, maxVideoSeconds, draft, onPaste, }: AnswerInputProps) => import("react").JSX.Element;
26
+ export declare const AnswerInput: ({ engine, assessmentId, kind, disabled, wordCountRule, strings, candidateCountryCode, region, maxVideoSeconds, draft, allowPaste, isFeedbackComment, onBlockedPaste, }: AnswerInputProps) => import("react").JSX.Element;
@@ -11,8 +11,12 @@ export type SapiaChatbotProps = {
11
11
  apiOrigin?: string;
12
12
  theme?: Theme;
13
13
  showHeader?: boolean;
14
+ /** Internal session (Sapia staff / QA): permits paste on every question. */
15
+ isInternalUser?: boolean;
14
16
  /** Interview language override (e.g. "fr"); defaults to the browser language. */
15
17
  language?: string;
18
+ /** Absolute http(s) URL offered as a "continue" CTA once the interview ends. */
19
+ redirectUrl?: string;
16
20
  /** Receives every widget event; or pass an emitter via createChatbot. */
17
21
  onEvent?: ChatbotEventHandler;
18
22
  /** @internal used by embed/createChatbot to share its emitter. */
@@ -20,4 +24,4 @@ export type SapiaChatbotProps = {
20
24
  /** @internal streaming transport override (mock SSE for the demo / tests). */
21
25
  fetchImpl?: typeof fetch;
22
26
  };
23
- export declare const SapiaChatbot: ({ env, region, appId, assessmentId, apiOrigin, theme, showHeader, language, onEvent, emitter, fetchImpl, }: SapiaChatbotProps) => import("react").JSX.Element;
27
+ export declare const SapiaChatbot: ({ env, region, appId, assessmentId, apiOrigin, theme, showHeader, isInternalUser, language, redirectUrl, onEvent, emitter, fetchImpl, }: SapiaChatbotProps) => import("react").JSX.Element;
@@ -1,15 +1,27 @@
1
1
  import { MessageBubble } from '../core/bubbles/types';
2
2
  import { ChatLogItem } from '../ui/chat/types';
3
+ /** Interview-wide conditions that freeze answers the candidate has already given. */
4
+ export type AnswerLocks = {
5
+ /** Terms accepted: consent is tied to the identity on file, so PII and SMS consent freeze. */
6
+ termsAccepted: boolean;
7
+ /** The final submit is through: the whole transcript is the submission, so nothing is editable. */
8
+ submitted: boolean;
9
+ };
3
10
  /** Per-card eyebrow + optional dark badge, keyed by agent bubble id. */
4
11
  export type QuestionCard = {
5
12
  label: string;
6
13
  badge?: string;
7
14
  };
8
- export declare const mapBubblesToChatItems: (bubbles: MessageBubble[], cards?: Map<string, QuestionCard>, streamingBubbleId?: string) => ChatLogItem[];
9
- export type TextRules = {
10
- minLength?: number;
11
- maxLength?: number;
12
- recommendedLength?: number;
15
+ export declare const mapBubblesToChatItems: (bubbles: MessageBubble[], cards?: Map<string, QuestionCard>, locks?: AnswerLocks, streamingBubbleId?: string) => ChatLogItem[];
16
+ /**
17
+ * Word bounds for the current question. The rule names say "TextLength" but the
18
+ * values are word counts — the backend interpolates them into the question text
19
+ * the candidate reads as "between {recommended} and {max} words".
20
+ */
21
+ export type WordCountRule = {
22
+ min?: number;
23
+ recommend?: number;
24
+ max?: number;
13
25
  };
14
26
  /**
15
27
  * The agent bubble whose answer the bottom input targets: the question under
@@ -61,6 +73,17 @@ export declare enum CurrentInputKind {
61
73
  VIDEO = "video"
62
74
  }
63
75
  export declare const currentInputKind: (bubbles: MessageBubble[], editingQuestionId?: string) => CurrentInputKind;
64
- export declare const currentTextRules: (bubbles: MessageBubble[], editingQuestionId?: string) => TextRules;
76
+ export declare const currentWordCountRule: (bubbles: MessageBubble[], editingQuestionId?: string) => WordCountRule;
77
+ /**
78
+ * Whether the question the composer currently targets opts out of the paste block. Reads the same
79
+ * bubble as the other composer selectors, so re-editing an earlier answer picks up that question's
80
+ * tag rather than the newest question's.
81
+ */
82
+ export declare const currentQuestionAllowsPaste: (bubbles: MessageBubble[], editingQuestionId?: string) => boolean;
83
+ /**
84
+ * Whether the composer currently targets the optional post-interview feedback comment. Sending
85
+ * nothing is a valid answer there, and it carries none of the word-count rules an FTQ does.
86
+ */
87
+ export declare const isFeedbackCommentQuestion: (bubbles: MessageBubble[], editingQuestionId?: string) => boolean;
65
88
  /** Max video answer length in seconds (legacy reuses the voice-seconds rule). */
66
89
  export declare const currentMaxVideoSeconds: (bubbles: MessageBubble[], editingQuestionId?: string) => number;
@@ -40,7 +40,6 @@ export declare const buildStrings: (t: Translator) => {
40
40
  description: string;
41
41
  kind: SummaryKind;
42
42
  };
43
- answerLooksGood: string;
44
43
  composerFooter: string;
45
44
  richToolbar: string;
46
45
  richBold: string;
@@ -48,6 +47,9 @@ export declare const buildStrings: (t: Translator) => {
48
47
  richBulletList: string;
49
48
  richNumberList: string;
50
49
  inputPlaceholder: string;
50
+ inputPlaceholderPaste: string;
51
+ /** Placeholder for the optional feedback comment, which says sending empty skips it. */
52
+ feedbackCommentPlaceholder: string;
51
53
  inputLabel: string;
52
54
  sendLabel: string;
53
55
  retryLabel: string;
@@ -56,12 +58,20 @@ export declare const buildStrings: (t: Translator) => {
56
58
  editingLabel: string;
57
59
  editBannerLabel: string;
58
60
  optionsGroupLabel: string;
61
+ /** Opens a folded run of intro hints. */
62
+ hintExpand: string;
63
+ /** Closes it again. */
64
+ hintCollapse: string;
59
65
  senderAgent: string;
60
66
  senderCandidate: string;
61
67
  opensNewTab: string;
62
68
  widgetLabel: string;
69
+ /**
70
+ * Accessible name for the rating slider, with no visible counterpart: the backend's own question
71
+ * ("Please rate your experience.") is the prompt, so a second line of widget copy saying the same
72
+ * thing read as a duplicate on screen and announced twice off it.
73
+ */
63
74
  sliderLabel: string;
64
- sliderHint: string;
65
75
  submitRatingLabel: string;
66
76
  dropdownSearchPlaceholder: string;
67
77
  dropdownSearchLabel: string;
@@ -73,7 +83,17 @@ export declare const buildStrings: (t: Translator) => {
73
83
  nameConfirmBody: (suggested: string) => string;
74
84
  useSuggestedLabel: (suggested: string) => string;
75
85
  useOriginalLabel: string;
76
- minLengthHint: (min: number) => string;
86
+ /** Composer word counter, e.g. "42 words". */
87
+ wordCount: (count: number) => string;
88
+ shortResponseHint: (min: number) => string;
89
+ goodResponseHint: string;
90
+ maxWordsHint: string;
91
+ shortAnswerTitle: (min: number) => string;
92
+ shortAnswerAim: (min: number, recommended?: number) => string;
93
+ shortAnswerEditAnytime: string;
94
+ shortAnswerEditNow: string;
95
+ dontShowAgainLabel: string;
96
+ continueLabel: string;
77
97
  loadingLabel: string;
78
98
  preparingLabel: string;
79
99
  doneTitle: (name?: string) => string;
@@ -81,7 +101,10 @@ export declare const buildStrings: (t: Translator) => {
81
101
  doneNextLabel: string;
82
102
  doneSteps: [string, string, string];
83
103
  doneRatingPrompt: string;
104
+ doneRedirect: string;
84
105
  startFailedMessage: string;
106
+ cohortClosedTitle: string;
107
+ cohortClosedMessage: string;
85
108
  /** PII validation error message for a given fi_error_pii* key. */
86
109
  piiError: (key: string) => string;
87
110
  videoOpenCamera: string;
@@ -117,5 +140,8 @@ export declare const buildStrings: (t: Translator) => {
117
140
  agcMessage: (attempt: number) => string;
118
141
  agcEdit: string;
119
142
  agcContinue: string;
143
+ editFailedTitle: string;
144
+ editFailedMessage: string;
145
+ editFailedDismiss: string;
120
146
  };
121
147
  export type UiStrings = ReturnType<typeof buildStrings>;
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  import { t as n } from "./defineProperty-DiFpLzd2.js";
4
4
  import { a as r, i, n as a, r as o, t as s } from "./classPrivateFieldSet2-DtQPURVD.js";
5
5
  import { t as c } from "./dist-FvHBWiRa.js";
@@ -455,4 +455,4 @@ var $ = Z({
455
455
  //#endregion
456
456
  export { $ as default };
457
457
 
458
- //# sourceMappingURL=react-DEF8h8hc.js.map
458
+ //# sourceMappingURL=react--hlAJW6n.js.map
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  import { t as n } from "./defineProperty-DiFpLzd2.js";
4
4
  import { a as r, i, n as a, r as o, t as s } from "./classPrivateFieldSet2-DtQPURVD.js";
5
5
  //#region ../../node_modules/.pnpm/twitch-video-element@0.1.6/node_modules/twitch-video-element/dist/twitch-video-element.js
@@ -365,4 +365,4 @@ var J = K({
365
365
  //#endregion
366
366
  export { J as default };
367
367
 
368
- //# sourceMappingURL=react-y4vaW3OH.js.map
368
+ //# sourceMappingURL=react-B6p_EGvt.js.map
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  import { t as n } from "./defineProperty-DiFpLzd2.js";
4
4
  import { a as r, i, n as a, r as o, t as s } from "./classPrivateFieldSet2-DtQPURVD.js";
5
5
  //#region ../../node_modules/.pnpm/super-media-element@1.4.2/node_modules/super-media-element/super-media-element.js
@@ -369,4 +369,4 @@ var P = M({
369
369
  //#endregion
370
370
  export { P as default };
371
371
 
372
- //# sourceMappingURL=react-DDjfkpOE.js.map
372
+ //# sourceMappingURL=react-BKQw6USG.js.map
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  import { t as n } from "./defineProperty-DiFpLzd2.js";
4
4
  import { a as r, i, n as a, r as o, t as s } from "./classPrivateFieldSet2-DtQPURVD.js";
5
5
  //#region ../../node_modules/.pnpm/tiktok-video-element@0.1.2/node_modules/tiktok-video-element/dist/tiktok-video-element.js
@@ -332,4 +332,4 @@ var K = W({
332
332
  //#endregion
333
333
  export { K as default };
334
334
 
335
- //# sourceMappingURL=react-qa12DkXH.js.map
335
+ //# sourceMappingURL=react-Bh9U4IBe.js.map
@@ -1,4 +1,4 @@
1
- import { S as e } from "./messages-DCvBcspv.js";
1
+ import { C as e } from "./messages-t5h3u6nr.js";
2
2
  //#region ../../node_modules/.pnpm/react@19.2.7/node_modules/react/cjs/react.production.js
3
3
  var t = /* @__PURE__ */ e(((e) => {
4
4
  var t = Symbol.for("react.transitional.element"), n = Symbol.for("react.portal"), r = Symbol.for("react.fragment"), i = Symbol.for("react.strict_mode"), a = Symbol.for("react.profiler"), o = Symbol.for("react.consumer"), s = Symbol.for("react.context"), c = Symbol.for("react.forward_ref"), l = Symbol.for("react.suspense"), u = Symbol.for("react.memo"), d = Symbol.for("react.lazy"), f = Symbol.for("react.activity"), p = Symbol.iterator;
@@ -292,4 +292,4 @@ var t = /* @__PURE__ */ e(((e) => {
292
292
  //#endregion
293
293
  export { n as t };
294
294
 
295
- //# sourceMappingURL=react-DOgLiyZi.js.map
295
+ //# sourceMappingURL=react-BtFG4mMU.js.map
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  import { t as n } from "./defineProperty-DiFpLzd2.js";
4
4
  import { n as r } from "./hls-mBncGwsE.js";
5
5
  import { a as i, i as a, n as o, r as s, t as c } from "./classPrivateFieldSet2-DtQPURVD.js";
@@ -215,4 +215,4 @@ var b = v({
215
215
  //#endregion
216
216
  export { b as default };
217
217
 
218
- //# sourceMappingURL=react-Bw5AugJc.js.map
218
+ //# sourceMappingURL=react-Bx1VLQqP.js.map
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  import { t as n } from "./defineProperty-DiFpLzd2.js";
4
4
  import { a as r, i, n as a, r as o, t as s } from "./classPrivateFieldSet2-DtQPURVD.js";
5
5
  import { t as c } from "./dist-FvHBWiRa.js";
@@ -2207,4 +2207,4 @@ var rt = tt({
2207
2207
  //#endregion
2208
2208
  export { rt as default };
2209
2209
 
2210
- //# sourceMappingURL=react-DPFikgTM.js.map
2210
+ //# sourceMappingURL=react-DFkljL-6.js.map
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  import { t as n } from "./defineProperty-DiFpLzd2.js";
4
4
  import { a as r, i, n as a, r as o, t as s } from "./classPrivateFieldSet2-DtQPURVD.js";
5
5
  //#region ../../node_modules/.pnpm/spotify-audio-element@1.0.4/node_modules/spotify-audio-element/dist/spotify-audio-element.js
@@ -309,4 +309,4 @@ var W = H({
309
309
  //#endregion
310
310
  export { W as default };
311
311
 
312
- //# sourceMappingURL=react-BjO903pN.js.map
312
+ //# sourceMappingURL=react-DJK2_GuR.js.map
@@ -1,5 +1,5 @@
1
- import { w as e } from "./messages-DCvBcspv.js";
2
- import { t } from "./react-DOgLiyZi.js";
1
+ import { T as e } from "./messages-t5h3u6nr.js";
2
+ import { t } from "./react-BtFG4mMU.js";
3
3
  import { t as n } from "./defineProperty-DiFpLzd2.js";
4
4
  import { i as r, n as i, t as a } from "./classPrivateFieldSet2-DtQPURVD.js";
5
5
  import { n as o, t as s } from "./dist-BOe6WyvN.js";
@@ -190,4 +190,4 @@ var S = b({
190
190
  //#endregion
191
191
  export { S as default };
192
192
 
193
- //# sourceMappingURL=react-D8hV9uow.js.map
193
+ //# sourceMappingURL=react-yXcg2glq.js.map
@@ -1,8 +1,8 @@
1
1
  import { ReactNode, Ref } from 'react';
2
2
  import { ChatLogItem } from '../types';
3
3
  export type SliderLabels = {
4
+ /** Accessible name for the range input — the backend's question is the visible prompt. */
4
5
  label: string;
5
- hint: string;
6
6
  submitLabel: string;
7
7
  };
8
8
  export type DropdownLabels = {
@@ -1,5 +1,6 @@
1
1
  import { ReactNode } from 'react';
2
2
  import { ChatBubbleLabels, DropdownLabels, MediaLabels, SliderLabels } from '../ChatBubble/ChatBubble';
3
+ import { HintDisclosureLabels } from '../HintDisclosure/HintDisclosure';
3
4
  import { ChatLogItem } from '../types';
4
5
  export type ChatLogProps = {
5
6
  items: ChatLogItem[];
@@ -11,6 +12,8 @@ export type ChatLogProps = {
11
12
  /** Phase-2 loading phrase (and the only one on the first agent turn). */
12
13
  typingThinkingLabel: string;
13
14
  bubbleLabels: ChatBubbleLabels;
15
+ /** Copy for the collapse/expand toggle on a folded hint run. */
16
+ hintLabels: HintDisclosureLabels;
14
17
  sliderLabels: SliderLabels;
15
18
  dropdownLabels: DropdownLabels;
16
19
  mediaLabels: MediaLabels;
@@ -32,4 +35,4 @@ export type ChatLogProps = {
32
35
  * are announced once through a separate visually-hidden live region (the transcript itself is
33
36
  * deliberately not one — see the announcer effect below).
34
37
  */
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;
38
+ export declare const ChatLog: ({ items, typing, typingLabel, typingReadingLabel, typingThinkingLabel, bubbleLabels, hintLabels, sliderLabels, dropdownLabels, mediaLabels, onRetry, onSelectOption, onSubmitSlider, onEdit, onRetryVideo, onResolveVideoUrl, cardFooter, cardFooterItemId, header, }: ChatLogProps) => import("react").JSX.Element;
@@ -0,0 +1,20 @@
1
+ import { ReactNode } from 'react';
2
+ export type HintDisclosureLabels = {
3
+ /** Opens the folded hints, e.g. "Read the full brief". */
4
+ expand: string;
5
+ /** Closes them again, e.g. "Show less". */
6
+ collapse: string;
7
+ };
8
+ export type HintDisclosureProps = {
9
+ /** The foldable hint bubbles — each renders its own <li>. */
10
+ children: ReactNode;
11
+ labels: HintDisclosureLabels;
12
+ /** Open state, owned by ChatLog: the bubble above needs it too, for the trailing ellipsis. */
13
+ expanded: boolean;
14
+ onToggle: () => void;
15
+ };
16
+ /**
17
+ * Progressive disclosure for a stack of agent hints. Open on arrival so the brief is read as it
18
+ * streams, then folded away once it has served its purpose.
19
+ */
20
+ export declare const HintDisclosure: ({ children, labels, expanded, onToggle }: HintDisclosureProps) => import("react").JSX.Element;
@@ -81,4 +81,9 @@ export type ChatLogItem = {
81
81
  editing?: boolean;
82
82
  /** Agent text is still streaming in; renders a blinking caret at the end. */
83
83
  streaming?: boolean;
84
+ /**
85
+ * An unanswerable agent aside. More than two in a row collapse behind a disclosure —
86
+ * a long intro brief otherwise pushes the first question off screen.
87
+ */
88
+ isHint?: boolean;
84
89
  };
@@ -1,3 +1,8 @@
1
+ /**
2
+ * Flat ceiling on an answer regardless of the question's word rule (legacy
3
+ * MAX_CHARACTER_COUNT). Guards the request body when a question ships no maximum.
4
+ */
5
+ export declare const MAX_ANSWER_CHARACTERS = 5000;
1
6
  /** Class scoping every style in dist/sapia-chatbot.css (see src/styles/index.css). */
2
7
  export declare const WIDGET_ROOT_CLASS = "sapia-chatbot";
3
8
  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";
@@ -0,0 +1,17 @@
1
+ import { RefObject } from 'react';
2
+ type UsePreventPasteParams = {
3
+ /** When true the hook is a no-op (paste is permitted). */
4
+ allowPaste: boolean;
5
+ /** Wrapper element of the composer; only events inside it are blocked. */
6
+ containerRef: RefObject<HTMLElement | null>;
7
+ /** Called for each blocked attempt (drives the INVALID_PASTE_ANSWER_ACTION log). */
8
+ onBlockedPaste?: () => void;
9
+ };
10
+ /**
11
+ * Blocks paste and drop inside a composer. Listeners are bound to `document` in the capture phase
12
+ * rather than to the element: assistive overlays such as ReciteMe re-parse the host DOM, which
13
+ * detaches per-node and React-synthetic handlers (legacy TG-21437). `beforeinput` covers keyboards
14
+ * that insert pasted text without firing a `paste` event.
15
+ */
16
+ export declare const usePreventPaste: ({ allowPaste, containerRef, onBlockedPaste, }: UsePreventPasteParams) => void;
17
+ export {};
@@ -8,7 +8,6 @@ export type SliderInputProps = {
8
8
  enabled?: boolean;
9
9
  pending?: boolean;
10
10
  label: string;
11
- hint: string;
12
11
  submitLabel: string;
13
12
  onSubmit?: (score: number) => void;
14
13
  };
@@ -16,4 +15,4 @@ export type SliderInputProps = {
16
15
  * Experience-rating slider (legacy renderSlider, AntD-free). Native range
17
16
  * input: keyboard accessible (arrow keys) out of the box.
18
17
  */
19
- export declare const SliderInput: ({ min, max, defaultValue, submittedValue, enabled, pending, label, hint, submitLabel, onSubmit, }: SliderInputProps) => import("react").JSX.Element;
18
+ export declare const SliderInput: ({ min, max, defaultValue, submittedValue, enabled, pending, label, submitLabel, onSubmit, }: SliderInputProps) => import("react").JSX.Element;
@@ -9,24 +9,41 @@ export type TextAnswerInputProps = {
9
9
  /** Receives the answer text — markdown in rich mode, plain text otherwise. */
10
10
  onSubmit: (text: string) => void;
11
11
  disabled?: boolean;
12
- /** Character rules from the current question (legacy RuleName min/max/recommended). */
13
- minLength?: number;
14
- maxLength?: number;
15
- recommendedLength?: number;
12
+ /**
13
+ * Permits sending an empty composer. Off by default — an empty answer is only meaningful where
14
+ * the question is optional (the post-interview feedback comment sends a skip).
15
+ */
16
+ allowEmptySubmit?: boolean;
17
+ /** Word bounds from the current question (the BE *TextLength rules are word counts). */
18
+ minWords?: number;
19
+ maxWords?: number;
20
+ recommendedWords?: number;
16
21
  placeholder: string;
17
22
  sendLabel: string;
18
- /** Hint shown while below minLength, e.g. "Please write at least {min} characters". */
19
- minLengthHint?: string;
20
- /** Affirmation shown once the minimum is met, e.g. "Looking good". */
21
- okHint?: string;
23
+ /**
24
+ * Called instead of onSubmit when the answer is under minWords, with the count and a
25
+ * submit callback to run if the candidate chooses to send it anyway. Omit to send short
26
+ * answers straight through — the minimum only ever warns, it never withholds Send.
27
+ */
28
+ onBelowMinimum?: (words: number, submit: () => void) => void;
29
+ /** Counter label for a word count, e.g. "42 words". */
30
+ wordCountLabel?: (count: number) => string;
31
+ /** Counter message while under minWords. */
32
+ shortResponseHint?: string;
33
+ /** Counter message between the minimum and the maximum. */
34
+ goodResponseHint?: string;
35
+ /** Counter message once maxWords is reached. */
36
+ maxWordsHint?: string;
22
37
  /** Reassurance caption under the composer. */
23
38
  footer?: string;
24
39
  inputLabel: string;
25
40
  /** Enables the rich-text editor + formatting toolbar (plain-text FTQ only). */
26
41
  richText?: boolean;
27
42
  toolbarLabels?: RichToolbarLabels;
28
- /** Called when the candidate pastes (AGC paste logging). */
29
- onPaste?: () => void;
43
+ /** Permits paste for this question (ALLOW_PASTE tag or an internal session). Blocked by default. */
44
+ allowPaste?: boolean;
45
+ /** Called for each blocked paste attempt (AGC paste logging). */
46
+ onBlockedPaste?: () => void;
30
47
  /** SC 1.3.5 token for PII questions (e.g. "email", "name"); omitted for free-text answers. */
31
48
  autoComplete?: string;
32
49
  /** Plain text the composer opens on (re-editing a submitted answer). */
@@ -36,4 +53,4 @@ export type TextAnswerInputProps = {
36
53
  /** Moves focus to the editor on mount (used when an edit opens the composer). */
37
54
  focusOnMount?: boolean;
38
55
  };
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;
56
+ export declare const TextAnswerInput: ({ onSubmit, disabled, allowEmptySubmit, minWords, maxWords, recommendedWords, placeholder, sendLabel, onBelowMinimum, wordCountLabel, shortResponseHint, goodResponseHint, maxWordsHint, footer, inputLabel, richText, toolbarLabels, allowPaste, onBlockedPaste, autoComplete, initialText, initialMarkdown, focusOnMount, }: TextAnswerInputProps) => import("react").JSX.Element;
@@ -11,6 +11,6 @@ export type EmbeddedVideoPlayerProps = {
11
11
  * (YouTube etc.) uses react-player. Lazy-loaded by ./EmbeddedVideo so react-player
12
12
  * and @vimeo/player land in a separate chunk (size budget, ADR-024/ADR-025).
13
13
  * Deviation from legacy: dropped the isVideoModuleOpen auto-pause coupling and the
14
- * fixed-pixel responsive sizing — the embed now fills the full bubble width.
14
+ * fixed-pixel responsive sizing — the player fills the frame ./EmbeddedVideo supplies.
15
15
  */
16
16
  export declare const EmbeddedVideoPlayer: ({ url, label }: EmbeddedVideoPlayerProps) => import("react").JSX.Element;
@@ -0,0 +1,24 @@
1
+ export declare enum MarkdownFlow {
2
+ /** No block wrapper — for copy that sits inside an existing heading, span or label. */
3
+ INLINE = "inline",
4
+ /** Paragraphs with the chat log's spacing, for multi-paragraph copy. */
5
+ BLOCK = "block"
6
+ }
7
+ export type MarkdownTextProps = {
8
+ /** Translated copy, possibly carrying markdown emphasis or links. */
9
+ children: string;
10
+ flow?: MarkdownFlow;
11
+ /**
12
+ * Label for the visually-hidden new-tab warning, e.g. "opens in a new tab". Supplying it is
13
+ * what makes links open in a new tab: without a warning to announce, a new tab would be an
14
+ * unannounced context change (SC 3.2.5), so those links stay in the current one.
15
+ */
16
+ opensNewTab?: string;
17
+ };
18
+ /**
19
+ * Renders translated copy as markdown — the backend writes emphasis and links into the
20
+ * translation file, so the raw template cannot go straight into the DOM. Raw HTML in the
21
+ * source is ignored rather than injected: react-markdown skips it without rehype-raw, so
22
+ * no sanitizer is needed here.
23
+ */
24
+ export declare const MarkdownText: ({ children, flow, opensNewTab, }: MarkdownTextProps) => import("react").JSX.Element;
@@ -0,0 +1,9 @@
1
+ export type CohortClosedScreenLabels = {
2
+ title: string;
3
+ message: string;
4
+ };
5
+ export type CohortClosedScreenProps = {
6
+ labels: CohortClosedScreenLabels;
7
+ };
8
+ /** Terminal screen for an assessment whose cohort has closed — nothing here can be answered. */
9
+ export declare const CohortClosedScreen: ({ labels }: CohortClosedScreenProps) => import("react").JSX.Element;
@@ -7,9 +7,12 @@ export type DoneScreenLabels = {
7
7
  };
8
8
  export type DoneScreenProps = {
9
9
  labels: DoneScreenLabels;
10
+ /** Pre-validated absolute URL to continue to; omitted when the host supplied none. */
11
+ redirectUrl?: string;
12
+ redirectLabel?: string;
10
13
  };
11
14
  /**
12
15
  * Completion screen shown when the interview has ended. The star rating is
13
16
  * decorative only (no submission) per the redesign scope decision.
14
17
  */
15
- export declare const DoneScreen: ({ labels }: DoneScreenProps) => import("react").JSX.Element;
18
+ export declare const DoneScreen: ({ labels, redirectUrl, redirectLabel }: DoneScreenProps) => import("react").JSX.Element;
@@ -9,5 +9,14 @@ export type HeaderProps = {
9
9
  };
10
10
  /**
11
11
  * Interview header: branding/title, a muted subtitle, and a progress pill.
12
+ *
13
+ * The right-hand end of the row always keeps a 44px slot free. Recite Me — and any other
14
+ * host-page accessibility overlay — pins its launcher to the top-right of the page, on top of
15
+ * whatever the widget drew there, and the count used to be the casualty.
16
+ *
17
+ * Below 640px of *container* width there is no room for a pill beside a truncating title, so the
18
+ * count renders as a subtitle suffix instead. Container width, not viewport: hosts embed the
19
+ * widget at arbitrary widths, so a media query would answer the wrong question. `px` and not
20
+ * `rem` so a host that changes the root font size cannot move the breakpoint.
12
21
  */
13
22
  export declare const Header: ({ title, subtitle, logoUrl, logoAlt, progressLabel, }: HeaderProps) => import("react").JSX.Element;
@@ -0,0 +1,10 @@
1
+ export type TerminalMessageScreenProps = {
2
+ /** Backend copy (markdown) explaining why the interview stopped. */
3
+ message: string;
4
+ };
5
+ /**
6
+ * Terminal screen for an interview the backend ended with an explanation rather than a
7
+ * completion — archived cohort or reused assessment. No title: the backend copy is a
8
+ * self-contained message.
9
+ */
10
+ export declare const TerminalMessageScreen: ({ message }: TerminalMessageScreenProps) => import("react").JSX.Element;