@xinghunm/ai-chat 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -21,6 +21,7 @@ var DEFAULT_AI_CHAT_LABELS = {
21
21
  sendButton: "Send",
22
22
  stopButton: "Stop",
23
23
  retryButton: "Retry",
24
+ scrollToLatest: "Jump to latest",
24
25
  placeholder: "Ask something...",
25
26
  modeLabelAsk: "Ask",
26
27
  modeLabelPlan: "Plan",
@@ -42,7 +43,10 @@ var DEFAULT_AI_CHAT_LABELS = {
42
43
  questionnaireSubmitting: "Submitting...",
43
44
  questionnaireSubmitted: "Selection submitted. Waiting for the plan to continue...",
44
45
  questionnaireValidationPrefix: "Please complete:",
45
- questionnaireSubmitFailed: "Failed to submit. Please try again."
46
+ questionnaireSubmitFailed: "Failed to submit. Please try again.",
47
+ questionnaireMultiSelectHint: "Multiple choice",
48
+ questionnaireOtherOptionLabel: "Other",
49
+ questionnaireOtherPlaceholder: "Other"
46
50
  };
47
51
 
48
52
  // src/store/chat-store.ts
@@ -695,7 +699,7 @@ var AiChatProvider = (props) => {
695
699
  };
696
700
 
697
701
  // src/components/chat-thread/index.tsx
698
- import { useCallback as useCallback3, useLayoutEffect as useLayoutEffect2, useMemo as useMemo4, useRef as useRef4, useState as useState4 } from "react";
702
+ import { useCallback as useCallback3, useLayoutEffect as useLayoutEffect2, useMemo as useMemo4, useRef as useRef5, useState as useState4 } from "react";
699
703
  import styled9 from "@emotion/styled";
700
704
 
701
705
  // src/context/use-chat-context.ts
@@ -1485,31 +1489,126 @@ var Value = styled3.span`
1485
1489
  `;
1486
1490
 
1487
1491
  // src/components/chat-thread/components/questionnaire-card.tsx
1488
- import { useState as useState2 } from "react";
1492
+ import {
1493
+ useEffect as useEffect3,
1494
+ useRef as useRef3,
1495
+ useState as useState2
1496
+ } from "react";
1489
1497
  import styled4 from "@emotion/styled";
1490
- import { jsx as jsx5, jsxs as jsxs3 } from "@emotion/react/jsx-runtime";
1498
+ import { InputField } from "@xinghunm/compass-ui";
1499
+
1500
+ // src/components/chat-thread/components/questionnaire-card-helpers.ts
1491
1501
  var OTHER_OPTION_VALUE = "__other__";
1492
- var DEFAULT_QUESTIONNAIRE_CARD_LABELS = {
1493
- submitting: "Submitting...",
1494
- submitted: "Selection submitted. Waiting for the plan to continue...",
1495
- validationPrefix: "Please complete:",
1496
- submitFailed: "Failed to submit. Please try again."
1497
- };
1498
- var createInitialAnswers = (questionnaire) => ({
1499
- ...questionnaire.answers ?? {}
1500
- });
1502
+ var getQuestionnaireQuestion = (questionnaire) => questionnaire.question;
1501
1503
  var getMultiSelectAnswerValues = (answer) => Array.isArray(answer) ? answer : [];
1502
- var getSingleSelectDraftState = (question, answer) => {
1504
+ var getQuestionOptionValues = (question) => new Set(question.options.map((option) => option.value));
1505
+ var extractSingleSelectOtherDraft = (question, answer) => {
1506
+ if (typeof answer !== "string") {
1507
+ return "";
1508
+ }
1509
+ return getQuestionOptionValues(question).has(answer) ? "" : answer;
1510
+ };
1511
+ var extractMultiSelectOtherDraft = (question, answer) => {
1512
+ if (!Array.isArray(answer)) {
1513
+ return "";
1514
+ }
1515
+ const optionValues = getQuestionOptionValues(question);
1516
+ const customValue = answer.find(
1517
+ (value) => typeof value === "string" && value !== OTHER_OPTION_VALUE && !optionValues.has(value)
1518
+ );
1519
+ return typeof customValue === "string" ? customValue : "";
1520
+ };
1521
+ var createInitialAnswers = (questionnaire) => {
1522
+ const initialAnswers = {};
1523
+ const question = getQuestionnaireQuestion(questionnaire);
1524
+ if (!question) {
1525
+ return initialAnswers;
1526
+ }
1527
+ const answer = questionnaire.answers?.[question.id];
1528
+ switch (question.kind) {
1529
+ case "single_select": {
1530
+ if (typeof answer !== "string") {
1531
+ break;
1532
+ }
1533
+ if (getQuestionOptionValues(question).has(answer)) {
1534
+ initialAnswers[question.id] = answer;
1535
+ break;
1536
+ }
1537
+ if (question.allowOther) {
1538
+ initialAnswers[question.id] = OTHER_OPTION_VALUE;
1539
+ }
1540
+ break;
1541
+ }
1542
+ case "multi_select": {
1543
+ if (!Array.isArray(answer)) {
1544
+ break;
1545
+ }
1546
+ const optionValues = getQuestionOptionValues(question);
1547
+ const selectedValues = [];
1548
+ let hasOtherValue = false;
1549
+ for (const value of answer) {
1550
+ if (typeof value !== "string") {
1551
+ continue;
1552
+ }
1553
+ if (optionValues.has(value)) {
1554
+ selectedValues.push(value);
1555
+ continue;
1556
+ }
1557
+ if (question.allowOther && !hasOtherValue) {
1558
+ selectedValues.push(OTHER_OPTION_VALUE);
1559
+ hasOtherValue = true;
1560
+ }
1561
+ }
1562
+ initialAnswers[question.id] = selectedValues;
1563
+ break;
1564
+ }
1565
+ default:
1566
+ initialAnswers[question.id] = answer;
1567
+ }
1568
+ return initialAnswers;
1569
+ };
1570
+ var createInitialOtherDrafts = (questionnaire) => {
1571
+ const drafts = {};
1572
+ const question = getQuestionnaireQuestion(questionnaire);
1573
+ if (!question) {
1574
+ return drafts;
1575
+ }
1576
+ const answer = questionnaire.answers?.[question.id];
1577
+ switch (question.kind) {
1578
+ case "single_select":
1579
+ if (question.allowOther) {
1580
+ drafts[question.id] = extractSingleSelectOtherDraft(question, answer);
1581
+ }
1582
+ break;
1583
+ case "multi_select":
1584
+ if (question.allowOther) {
1585
+ drafts[question.id] = extractMultiSelectOtherDraft(question, answer);
1586
+ }
1587
+ break;
1588
+ default:
1589
+ break;
1590
+ }
1591
+ return drafts;
1592
+ };
1593
+ var getMultiSelectDraftState = (question, answer, otherDraft) => {
1594
+ const answerValues = getMultiSelectAnswerValues(answer);
1595
+ return {
1596
+ selectedValues: answerValues,
1597
+ otherValue: otherDraft,
1598
+ hasOtherSelected: question.allowOther === true && answerValues.includes(OTHER_OPTION_VALUE)
1599
+ };
1600
+ };
1601
+ var getSingleSelectDraftState = (question, answer, otherDraft) => {
1503
1602
  if (typeof answer !== "string") {
1504
1603
  return {
1505
1604
  selectedValue: void 0,
1506
- otherValue: ""
1605
+ otherValue: otherDraft
1507
1606
  };
1508
1607
  }
1509
- const matchesOption = question.options.some((option) => option.value === answer);
1608
+ const matchesOption = answer !== OTHER_OPTION_VALUE && question.options.some((option) => option.value === answer);
1510
1609
  return {
1511
1610
  selectedValue: matchesOption ? answer : question.allowOther ? OTHER_OPTION_VALUE : void 0,
1512
- otherValue: matchesOption ? "" : answer
1611
+ otherValue: otherDraft
1513
1612
  };
1514
1613
  };
1515
1614
  var updateAnswerValue = (current, questionId, value) => ({
@@ -1521,6 +1620,17 @@ var toggleMultiSelectAnswer = (current, questionId, optionValue) => {
1521
1620
  const nextValues = currentValues.includes(optionValue) ? currentValues.filter((value) => value !== optionValue) : [...currentValues, optionValue];
1522
1621
  return updateAnswerValue(current, questionId, nextValues);
1523
1622
  };
1623
+ var toggleMultiSelectOtherAnswer = (current, question) => {
1624
+ const currentValues = getMultiSelectAnswerValues(current[question.id]);
1625
+ if (currentValues.includes(OTHER_OPTION_VALUE)) {
1626
+ return updateAnswerValue(
1627
+ current,
1628
+ question.id,
1629
+ currentValues.filter((value) => value !== OTHER_OPTION_VALUE)
1630
+ );
1631
+ }
1632
+ return updateAnswerValue(current, question.id, [...currentValues, OTHER_OPTION_VALUE]);
1633
+ };
1524
1634
  var getTextInputValue = (answer) => typeof answer === "string" ? String(answer) : "";
1525
1635
  var getNumberInputValue = (answer) => typeof answer === "number" || typeof answer === "string" ? String(answer) : "";
1526
1636
  var getOptionChoiceLabel = (index) => {
@@ -1529,20 +1639,49 @@ var getOptionChoiceLabel = (index) => {
1529
1639
  }
1530
1640
  return String(index + 1);
1531
1641
  };
1532
- var isMissingRequiredAnswer = (question, answers) => {
1533
- const answer = answers[question.id];
1642
+ var normalizeQuestionAnswer = (question, answer, otherDraft = "") => {
1643
+ if (answer === void 0) {
1644
+ return void 0;
1645
+ }
1534
1646
  switch (question.kind) {
1535
- case "boolean":
1536
- return typeof answer !== "boolean";
1537
1647
  case "multi_select":
1538
- return !Array.isArray(answer) || answer.length === 0;
1539
- case "number":
1540
- return typeof answer !== "number" || Number.isNaN(answer);
1541
- case "text":
1648
+ if (!Array.isArray(answer)) {
1649
+ return void 0;
1650
+ }
1651
+ return (() => {
1652
+ const optionValues = getQuestionOptionValues(question);
1653
+ const normalizedOtherDraft = otherDraft.trim();
1654
+ const normalizedValues = answer.flatMap((value) => {
1655
+ if (typeof value !== "string") {
1656
+ return [];
1657
+ }
1658
+ if (optionValues.has(value)) {
1659
+ return [value];
1660
+ }
1661
+ if (!question.allowOther || value !== OTHER_OPTION_VALUE) {
1662
+ return [];
1663
+ }
1664
+ return normalizedOtherDraft === "" ? [] : [normalizedOtherDraft];
1665
+ });
1666
+ return normalizedValues.length > 0 ? normalizedValues : void 0;
1667
+ })();
1542
1668
  case "single_select":
1543
- return typeof answer !== "string" || answer.trim() === "";
1669
+ if (answer === OTHER_OPTION_VALUE) {
1670
+ const normalizedOtherDraft = otherDraft.trim();
1671
+ return normalizedOtherDraft === "" ? void 0 : normalizedOtherDraft;
1672
+ }
1673
+ if (typeof answer !== "string" || answer.trim() === "") {
1674
+ return void 0;
1675
+ }
1676
+ return getQuestionOptionValues(question).has(answer) ? answer : void 0;
1677
+ case "text":
1678
+ return typeof answer === "string" && answer.trim() !== "" ? answer : void 0;
1679
+ case "number":
1680
+ return typeof answer === "number" && !Number.isNaN(answer) ? answer : void 0;
1681
+ case "boolean":
1682
+ return typeof answer === "boolean" ? answer : void 0;
1544
1683
  default:
1545
- return true;
1684
+ return answer;
1546
1685
  }
1547
1686
  };
1548
1687
  var formatQuestionAnswer = (question, answer) => {
@@ -1570,80 +1709,221 @@ var formatQuestionAnswer = (question, answer) => {
1570
1709
  return String(answer);
1571
1710
  }
1572
1711
  };
1573
- var normalizeQuestionAnswer = (question, answer) => {
1712
+ var buildQuestionSubmissionDetail = (question, answer) => {
1574
1713
  if (answer === void 0) {
1575
1714
  return void 0;
1576
1715
  }
1577
1716
  switch (question.kind) {
1578
- case "multi_select":
1579
- return Array.isArray(answer) && answer.length > 0 ? answer : void 0;
1580
- case "single_select":
1581
- case "text":
1582
- return typeof answer === "string" && answer.trim() !== "" ? answer : void 0;
1583
- case "number":
1584
- return typeof answer === "number" && !Number.isNaN(answer) ? answer : void 0;
1585
- case "boolean":
1586
- return typeof answer === "boolean" ? answer : void 0;
1717
+ case "single_select": {
1718
+ if (typeof answer !== "string") {
1719
+ return void 0;
1720
+ }
1721
+ const optionValues = getQuestionOptionValues(question);
1722
+ const trimmedAnswer = answer.trim();
1723
+ return {
1724
+ questionId: question.id,
1725
+ kind: question.kind,
1726
+ value: answer,
1727
+ selectedOptionValues: optionValues.has(answer) ? [answer] : [],
1728
+ otherValue: optionValues.has(answer) || trimmedAnswer === "" ? void 0 : trimmedAnswer
1729
+ };
1730
+ }
1731
+ case "multi_select": {
1732
+ if (!Array.isArray(answer)) {
1733
+ return void 0;
1734
+ }
1735
+ const optionValues = getQuestionOptionValues(question);
1736
+ const selectedOptionValues = answer.filter(
1737
+ (value) => typeof value === "string" && optionValues.has(value)
1738
+ );
1739
+ const otherValue = answer.find(
1740
+ (value) => typeof value === "string" && !optionValues.has(value)
1741
+ );
1742
+ return {
1743
+ questionId: question.id,
1744
+ kind: question.kind,
1745
+ value: answer,
1746
+ selectedOptionValues,
1747
+ otherValue: otherValue?.trim() ? otherValue.trim() : void 0
1748
+ };
1749
+ }
1587
1750
  default:
1588
- return answer;
1751
+ return {
1752
+ questionId: question.id,
1753
+ kind: question.kind,
1754
+ value: answer
1755
+ };
1756
+ }
1757
+ };
1758
+ var getMissingRequiredQuestions = (questionnaire, answers, otherDrafts) => {
1759
+ const question = getQuestionnaireQuestion(questionnaire);
1760
+ if (!question || !question.required) {
1761
+ return [];
1589
1762
  }
1763
+ return normalizeQuestionAnswer(question, answers[question.id], otherDrafts[question.id]) === void 0 ? [question] : [];
1764
+ };
1765
+ var prepareQuestionnaireSubmission = (questionnaire, answers, otherDrafts) => {
1766
+ const question = getQuestionnaireQuestion(questionnaire);
1767
+ if (!question) {
1768
+ return {
1769
+ normalizedAnswers: {},
1770
+ submissionDetails: void 0,
1771
+ content: questionnaire.title ?? "Questionnaire responses"
1772
+ };
1773
+ }
1774
+ const value = normalizeQuestionAnswer(question, answers[question.id], otherDrafts[question.id]);
1775
+ const normalizedAnswers = value === void 0 ? {} : { [question.id]: value };
1776
+ const detail = buildQuestionSubmissionDetail(question, normalizedAnswers[question.id]);
1777
+ const submissionDetails = detail === void 0 ? void 0 : { [question.id]: detail };
1778
+ return {
1779
+ normalizedAnswers,
1780
+ submissionDetails,
1781
+ content: [
1782
+ questionnaire.title ?? "Questionnaire responses",
1783
+ ...normalizedAnswers[question.id] === void 0 ? [] : [
1784
+ `- ${question.label}: ${formatQuestionAnswer(question, normalizedAnswers[question.id])}`
1785
+ ]
1786
+ ].join("\n")
1787
+ };
1788
+ };
1789
+ var getQuestionnaireStateKey = (questionnaire) => JSON.stringify([
1790
+ questionnaire.questionnaireId,
1791
+ questionnaire.blockKey,
1792
+ questionnaire.question,
1793
+ questionnaire.status,
1794
+ questionnaire.statusMessage
1795
+ ]);
1796
+
1797
+ // src/components/chat-thread/components/questionnaire-card.tsx
1798
+ import { jsx as jsx5, jsxs as jsxs3 } from "@emotion/react/jsx-runtime";
1799
+ var DEFAULT_QUESTIONNAIRE_CARD_LABELS = {
1800
+ submitting: "Submitting...",
1801
+ submitted: "Selection submitted. Waiting for the plan to continue...",
1802
+ validationPrefix: "Please complete:",
1803
+ submitFailed: "Failed to submit. Please try again.",
1804
+ multiSelectHint: "Multiple choice",
1805
+ otherOptionLabel: "Other",
1806
+ otherPlaceholder: "Other"
1807
+ };
1808
+ var stopInputClickPropagation = (event) => {
1809
+ event.stopPropagation();
1810
+ };
1811
+ var stopInputKeyPropagation = (event) => {
1812
+ event.stopPropagation();
1590
1813
  };
1814
+ var OptionChoice = ({
1815
+ questionId,
1816
+ optionLabel,
1817
+ index,
1818
+ isSelected,
1819
+ isInteractionLocked,
1820
+ onClick,
1821
+ inlineInput,
1822
+ tone = "default"
1823
+ }) => /* @__PURE__ */ jsxs3(
1824
+ OptionChoiceItem,
1825
+ {
1826
+ role: "button",
1827
+ tabIndex: isInteractionLocked ? -1 : 0,
1828
+ "aria-pressed": isSelected,
1829
+ "data-selected": isSelected,
1830
+ "data-tone": tone,
1831
+ "data-testid": `question-option-${questionId}-${index}`,
1832
+ onClick: (event) => {
1833
+ if (isInteractionLocked) {
1834
+ return;
1835
+ }
1836
+ if (event.target instanceof HTMLElement && event.target.closest("input")) {
1837
+ return;
1838
+ }
1839
+ onClick();
1840
+ },
1841
+ onKeyDown: (event) => {
1842
+ if (isInteractionLocked) {
1843
+ return;
1844
+ }
1845
+ if (event.key !== "Enter" && event.key !== " ") {
1846
+ return;
1847
+ }
1848
+ event.preventDefault();
1849
+ onClick();
1850
+ },
1851
+ children: [
1852
+ /* @__PURE__ */ jsx5(OptionChoiceMarker, { "data-selected": isSelected, children: getOptionChoiceLabel(index) }),
1853
+ /* @__PURE__ */ jsxs3(OptionChoiceContent, { children: [
1854
+ inlineInput ? null : /* @__PURE__ */ jsx5(OptionChoiceLabel, { children: optionLabel }),
1855
+ inlineInput
1856
+ ] })
1857
+ ]
1858
+ }
1859
+ );
1591
1860
  var QuestionnaireCardInner = ({
1592
1861
  questionnaire,
1593
1862
  interactive = false,
1594
1863
  onSubmit,
1595
1864
  labels
1596
1865
  }) => {
1866
+ const questionnaireRef = useRef3(questionnaire);
1867
+ const otherInputRefs = useRef3({});
1597
1868
  const [answers, setAnswers] = useState2(
1598
1869
  () => createInitialAnswers(questionnaire)
1599
1870
  );
1871
+ const [otherDrafts, setOtherDrafts] = useState2(
1872
+ () => createInitialOtherDrafts(questionnaire)
1873
+ );
1600
1874
  const [errorMessage, setErrorMessage] = useState2(null);
1601
1875
  const [isSubmitting, setIsSubmitting] = useState2(false);
1602
1876
  const [isSubmitted, setIsSubmitted] = useState2(false);
1877
+ const [pendingFocusQuestionId, setPendingFocusQuestionId] = useState2(null);
1603
1878
  const resolvedLabels = {
1604
1879
  ...DEFAULT_QUESTIONNAIRE_CARD_LABELS,
1605
1880
  ...labels
1606
1881
  };
1607
1882
  const hasExternalFailureStatus = questionnaire.status === "expired" || questionnaire.status === "failed";
1883
+ const question = getQuestionnaireQuestion(questionnaire);
1608
1884
  const visibleErrorMessage = questionnaire.statusMessage ?? errorMessage;
1609
1885
  const isInteractionLocked = !interactive || isSubmitting || isSubmitted || hasExternalFailureStatus;
1886
+ questionnaireRef.current = questionnaire;
1887
+ useEffect3(() => {
1888
+ setAnswers(createInitialAnswers(questionnaireRef.current));
1889
+ setOtherDrafts(createInitialOtherDrafts(questionnaireRef.current));
1890
+ }, [questionnaire.answers]);
1891
+ useEffect3(() => {
1892
+ if (!pendingFocusQuestionId || isInteractionLocked) {
1893
+ return;
1894
+ }
1895
+ const inputElement = otherInputRefs.current[pendingFocusQuestionId];
1896
+ if (!inputElement) {
1897
+ return;
1898
+ }
1899
+ inputElement.focus();
1900
+ setPendingFocusQuestionId(null);
1901
+ }, [isInteractionLocked, pendingFocusQuestionId]);
1610
1902
  const handleSubmit = async () => {
1611
1903
  if (isSubmitting || isSubmitted) {
1612
1904
  return;
1613
1905
  }
1614
- const missingQuestions = questionnaire.questions.filter(
1615
- (question) => question.required && isMissingRequiredAnswer(question, answers)
1616
- );
1906
+ const missingQuestions = getMissingRequiredQuestions(questionnaire, answers, otherDrafts);
1617
1907
  if (missingQuestions.length > 0) {
1618
1908
  setErrorMessage(
1619
- `${resolvedLabels.validationPrefix} ${missingQuestions.map((question) => question.label).join(", ")}`
1909
+ `${resolvedLabels.validationPrefix} ${missingQuestions.map((question2) => question2.label).join(", ")}`
1620
1910
  );
1621
1911
  return;
1622
1912
  }
1623
1913
  setErrorMessage(null);
1624
1914
  setIsSubmitting(true);
1625
- const normalizedAnswers = Object.fromEntries(
1626
- questionnaire.questions.flatMap((question) => {
1627
- const value = normalizeQuestionAnswer(question, answers[question.id]);
1628
- return value === void 0 ? [] : [[question.id, value]];
1629
- })
1915
+ const { normalizedAnswers, submissionDetails, content } = prepareQuestionnaireSubmission(
1916
+ questionnaire,
1917
+ answers,
1918
+ otherDrafts
1630
1919
  );
1631
- const contentLines = [
1632
- questionnaire.title ?? "Questionnaire responses",
1633
- ...questionnaire.questions.flatMap((question) => {
1634
- const value = normalizedAnswers[question.id];
1635
- if (value === void 0) {
1636
- return [];
1637
- }
1638
- return [`- ${question.label}: ${formatQuestionAnswer(question, value)}`];
1639
- })
1640
- ];
1641
1920
  try {
1642
1921
  await onSubmit?.({
1643
1922
  questionnaireId: questionnaire.questionnaireId,
1644
1923
  ...questionnaire.blockKey ? { blockKey: questionnaire.blockKey } : {},
1645
1924
  answers: normalizedAnswers,
1646
- content: contentLines.join("\n")
1925
+ details: submissionDetails,
1926
+ content
1647
1927
  });
1648
1928
  setIsSubmitted(true);
1649
1929
  } catch (error) {
@@ -1652,125 +1932,156 @@ var QuestionnaireCardInner = ({
1652
1932
  setIsSubmitting(false);
1653
1933
  }
1654
1934
  };
1655
- const renderQuestion = (question) => {
1656
- const renderOptionChoice = ({
1657
- questionId,
1658
- optionLabel,
1659
- index,
1660
- isSelected,
1661
- onClick,
1662
- inlineInput,
1663
- tone = "default"
1664
- }) => /* @__PURE__ */ jsxs3(
1665
- OptionChoiceItem,
1666
- {
1667
- role: "button",
1668
- tabIndex: isInteractionLocked ? -1 : 0,
1669
- "aria-pressed": isSelected,
1670
- "data-selected": isSelected,
1671
- "data-tone": tone,
1672
- "data-testid": `question-option-${questionId}-${index}`,
1673
- onClick: (event) => {
1674
- if (isInteractionLocked) {
1675
- return;
1676
- }
1677
- if (event.target instanceof HTMLElement && event.target.closest("input")) {
1678
- return;
1679
- }
1680
- onClick();
1681
- },
1682
- onKeyDown: (event) => {
1683
- if (isInteractionLocked) {
1684
- return;
1685
- }
1686
- if (event.key !== "Enter" && event.key !== " ") {
1687
- return;
1688
- }
1689
- event.preventDefault();
1690
- onClick();
1691
- },
1692
- children: [
1693
- /* @__PURE__ */ jsx5(OptionChoiceMarker, { "data-selected": isSelected, children: getOptionChoiceLabel(index) }),
1694
- /* @__PURE__ */ jsxs3(OptionChoiceContent, { children: [
1695
- inlineInput ? null : /* @__PURE__ */ jsx5(OptionChoiceLabel, { children: optionLabel }),
1696
- inlineInput
1697
- ] })
1698
- ]
1699
- },
1700
- `${questionId}-${optionLabel}`
1701
- );
1702
- switch (question.kind) {
1703
- case "multi_select":
1704
- return /* @__PURE__ */ jsx5(QuestionBody, { children: /* @__PURE__ */ jsx5(OptionList, { children: question.options.map((option, index) => {
1705
- const selectedValues = getMultiSelectAnswerValues(answers[question.id]);
1706
- const isSelected = selectedValues.includes(option.value);
1707
- return renderOptionChoice({
1708
- questionId: question.id,
1709
- optionLabel: option.label,
1710
- index,
1711
- isSelected,
1712
- onClick: () => setAnswers(
1713
- (current) => toggleMultiSelectAnswer(current, question.id, option.value)
1714
- )
1715
- });
1716
- }) }) });
1935
+ if (!question) {
1936
+ return null;
1937
+ }
1938
+ const renderQuestion = (questionToRender) => {
1939
+ switch (questionToRender.kind) {
1940
+ case "multi_select": {
1941
+ const multiSelectDraft = getMultiSelectDraftState(
1942
+ questionToRender,
1943
+ answers[questionToRender.id],
1944
+ otherDrafts[questionToRender.id] ?? ""
1945
+ );
1946
+ return /* @__PURE__ */ jsx5(QuestionBody, { children: /* @__PURE__ */ jsxs3(OptionList, { children: [
1947
+ questionToRender.options.map((option, index) => {
1948
+ const isSelected = multiSelectDraft.selectedValues.includes(option.value);
1949
+ return /* @__PURE__ */ jsx5(
1950
+ OptionChoice,
1951
+ {
1952
+ questionId: questionToRender.id,
1953
+ optionLabel: option.label,
1954
+ index,
1955
+ isSelected,
1956
+ isInteractionLocked,
1957
+ onClick: () => setAnswers(
1958
+ (current) => toggleMultiSelectAnswer(current, questionToRender.id, option.value)
1959
+ )
1960
+ },
1961
+ option.value
1962
+ );
1963
+ }),
1964
+ questionToRender.allowOther ? /* @__PURE__ */ jsx5(
1965
+ OptionChoice,
1966
+ {
1967
+ questionId: questionToRender.id,
1968
+ optionLabel: resolvedLabels.otherOptionLabel,
1969
+ index: questionToRender.options.length,
1970
+ isSelected: multiSelectDraft.hasOtherSelected,
1971
+ isInteractionLocked,
1972
+ tone: "other",
1973
+ onClick: () => {
1974
+ if (!multiSelectDraft.hasOtherSelected) {
1975
+ setPendingFocusQuestionId(questionToRender.id);
1976
+ }
1977
+ setAnswers((current) => toggleMultiSelectOtherAnswer(current, questionToRender));
1978
+ },
1979
+ inlineInput: multiSelectDraft.hasOtherSelected ? /* @__PURE__ */ jsx5(
1980
+ InlineOtherInput,
1981
+ {
1982
+ ref: (node) => {
1983
+ otherInputRefs.current[questionToRender.id] = node;
1984
+ },
1985
+ "data-testid": `question-input-${questionToRender.id}`,
1986
+ type: "text",
1987
+ value: multiSelectDraft.otherValue,
1988
+ placeholder: resolvedLabels.otherPlaceholder,
1989
+ readOnly: isInteractionLocked,
1990
+ onClick: stopInputClickPropagation,
1991
+ onKeyDown: stopInputKeyPropagation,
1992
+ onChange: (event) => {
1993
+ setOtherDrafts((current) => ({
1994
+ ...current,
1995
+ [questionToRender.id]: event.target.value
1996
+ }));
1997
+ }
1998
+ }
1999
+ ) : null
2000
+ },
2001
+ `${questionToRender.id}-other`
2002
+ ) : null
2003
+ ] }) });
2004
+ }
1717
2005
  case "single_select": {
1718
- const singleSelectDraft = getSingleSelectDraftState(question, answers[question.id]);
2006
+ const singleSelectDraft = getSingleSelectDraftState(
2007
+ questionToRender,
2008
+ answers[questionToRender.id],
2009
+ otherDrafts[questionToRender.id] ?? ""
2010
+ );
1719
2011
  return /* @__PURE__ */ jsx5(QuestionBody, { children: /* @__PURE__ */ jsxs3(OptionList, { children: [
1720
- question.options.map((option, index) => {
2012
+ questionToRender.options.map((option, index) => {
1721
2013
  const isSelected = singleSelectDraft.selectedValue === option.value;
1722
- return renderOptionChoice({
1723
- questionId: question.id,
1724
- optionLabel: option.label,
1725
- index,
1726
- isSelected,
1727
- onClick: () => setAnswers((current) => updateAnswerValue(current, question.id, option.value))
1728
- });
1729
- }),
1730
- question.allowOther ? renderOptionChoice({
1731
- questionId: question.id,
1732
- optionLabel: "Other",
1733
- index: question.options.length,
1734
- isSelected: singleSelectDraft.selectedValue === OTHER_OPTION_VALUE,
1735
- tone: "other",
1736
- onClick: () => setAnswers(
1737
- (current) => updateAnswerValue(current, question.id, singleSelectDraft.otherValue)
1738
- ),
1739
- inlineInput: singleSelectDraft.selectedValue === OTHER_OPTION_VALUE ? /* @__PURE__ */ jsx5(
1740
- InlineOtherInput,
2014
+ return /* @__PURE__ */ jsx5(
2015
+ OptionChoice,
1741
2016
  {
1742
- "data-testid": `question-input-${question.id}`,
1743
- type: "text",
1744
- value: singleSelectDraft.otherValue,
1745
- placeholder: "Other",
1746
- readOnly: isInteractionLocked,
1747
- onClick: (event) => {
1748
- event.stopPropagation();
1749
- },
1750
- onKeyDown: (event) => {
1751
- event.stopPropagation();
1752
- },
1753
- onChange: (event) => {
1754
- setAnswers(
1755
- (current) => updateAnswerValue(current, question.id, event.target.value)
1756
- );
2017
+ questionId: questionToRender.id,
2018
+ optionLabel: option.label,
2019
+ index,
2020
+ isSelected,
2021
+ isInteractionLocked,
2022
+ onClick: () => setAnswers(
2023
+ (current) => updateAnswerValue(current, questionToRender.id, option.value)
2024
+ )
2025
+ },
2026
+ option.value
2027
+ );
2028
+ }),
2029
+ questionToRender.allowOther ? /* @__PURE__ */ jsx5(
2030
+ OptionChoice,
2031
+ {
2032
+ questionId: questionToRender.id,
2033
+ optionLabel: resolvedLabels.otherOptionLabel,
2034
+ index: questionToRender.options.length,
2035
+ isSelected: singleSelectDraft.selectedValue === OTHER_OPTION_VALUE,
2036
+ isInteractionLocked,
2037
+ tone: "other",
2038
+ onClick: () => {
2039
+ if (singleSelectDraft.selectedValue !== OTHER_OPTION_VALUE) {
2040
+ setPendingFocusQuestionId(questionToRender.id);
1757
2041
  }
1758
- }
1759
- ) : null
1760
- }) : null
2042
+ setAnswers(
2043
+ (current) => updateAnswerValue(current, questionToRender.id, OTHER_OPTION_VALUE)
2044
+ );
2045
+ },
2046
+ inlineInput: singleSelectDraft.selectedValue === OTHER_OPTION_VALUE ? /* @__PURE__ */ jsx5(
2047
+ InlineOtherInput,
2048
+ {
2049
+ ref: (node) => {
2050
+ otherInputRefs.current[questionToRender.id] = node;
2051
+ },
2052
+ "data-testid": `question-input-${questionToRender.id}`,
2053
+ type: "text",
2054
+ value: singleSelectDraft.otherValue,
2055
+ placeholder: resolvedLabels.otherPlaceholder,
2056
+ readOnly: isInteractionLocked,
2057
+ onClick: stopInputClickPropagation,
2058
+ onKeyDown: stopInputKeyPropagation,
2059
+ onChange: (event) => {
2060
+ setOtherDrafts((current) => ({
2061
+ ...current,
2062
+ [questionToRender.id]: event.target.value
2063
+ }));
2064
+ }
2065
+ }
2066
+ ) : null
2067
+ },
2068
+ `${questionToRender.id}-other`
2069
+ ) : null
1761
2070
  ] }) });
1762
2071
  }
1763
2072
  case "text":
1764
2073
  return /* @__PURE__ */ jsx5(QuestionBody, { children: /* @__PURE__ */ jsx5(
1765
2074
  TextInput,
1766
2075
  {
1767
- "data-testid": `question-input-${question.id}`,
2076
+ "data-testid": `question-input-${questionToRender.id}`,
1768
2077
  type: "text",
1769
- value: getTextInputValue(answers[question.id]),
1770
- placeholder: question.placeholder,
2078
+ value: getTextInputValue(answers[questionToRender.id]),
2079
+ placeholder: questionToRender.placeholder,
1771
2080
  readOnly: isInteractionLocked,
1772
2081
  onChange: (event) => {
1773
- setAnswers((current) => updateAnswerValue(current, question.id, event.target.value));
2082
+ setAnswers(
2083
+ (current) => updateAnswerValue(current, questionToRender.id, event.target.value)
2084
+ );
1774
2085
  }
1775
2086
  }
1776
2087
  ) });
@@ -1779,55 +2090,63 @@ var QuestionnaireCardInner = ({
1779
2090
  /* @__PURE__ */ jsx5(
1780
2091
  TextInput,
1781
2092
  {
1782
- "data-testid": `question-input-${question.id}`,
2093
+ "data-testid": `question-input-${questionToRender.id}`,
1783
2094
  type: "number",
1784
- value: getNumberInputValue(answers[question.id]),
1785
- placeholder: question.placeholder,
2095
+ value: getNumberInputValue(answers[questionToRender.id]),
2096
+ placeholder: questionToRender.placeholder,
1786
2097
  readOnly: isInteractionLocked,
1787
2098
  onChange: (event) => {
1788
2099
  setAnswers(
1789
2100
  (current) => updateAnswerValue(
1790
2101
  current,
1791
- question.id,
2102
+ questionToRender.id,
1792
2103
  event.target.value === "" ? void 0 : Number(event.target.value)
1793
2104
  )
1794
2105
  );
1795
2106
  }
1796
2107
  }
1797
2108
  ),
1798
- question.unit ? /* @__PURE__ */ jsx5(Unit, { children: question.unit }) : null
2109
+ questionToRender.unit ? /* @__PURE__ */ jsx5(Unit, { children: questionToRender.unit }) : null
1799
2110
  ] }) });
1800
2111
  case "boolean":
1801
2112
  return /* @__PURE__ */ jsx5(QuestionBody, { children: /* @__PURE__ */ jsxs3(OptionList, { children: [
1802
- renderOptionChoice({
1803
- questionId: question.id,
1804
- optionLabel: question.trueLabel ?? "Yes",
1805
- index: 0,
1806
- isSelected: answers[question.id] === true,
1807
- onClick: () => setAnswers((current) => updateAnswerValue(current, question.id, true))
1808
- }),
1809
- renderOptionChoice({
1810
- questionId: question.id,
1811
- optionLabel: question.falseLabel ?? "No",
1812
- index: 1,
1813
- isSelected: answers[question.id] === false,
1814
- onClick: () => setAnswers((current) => updateAnswerValue(current, question.id, false))
1815
- })
2113
+ /* @__PURE__ */ jsx5(
2114
+ OptionChoice,
2115
+ {
2116
+ questionId: questionToRender.id,
2117
+ optionLabel: questionToRender.trueLabel ?? "Yes",
2118
+ index: 0,
2119
+ isSelected: answers[questionToRender.id] === true,
2120
+ isInteractionLocked,
2121
+ onClick: () => setAnswers((current) => updateAnswerValue(current, questionToRender.id, true))
2122
+ }
2123
+ ),
2124
+ /* @__PURE__ */ jsx5(
2125
+ OptionChoice,
2126
+ {
2127
+ questionId: questionToRender.id,
2128
+ optionLabel: questionToRender.falseLabel ?? "No",
2129
+ index: 1,
2130
+ isSelected: answers[questionToRender.id] === false,
2131
+ isInteractionLocked,
2132
+ onClick: () => setAnswers((current) => updateAnswerValue(current, questionToRender.id, false))
2133
+ }
2134
+ )
1816
2135
  ] }) });
1817
2136
  default:
1818
2137
  return null;
1819
2138
  }
1820
2139
  };
1821
2140
  return /* @__PURE__ */ jsxs3(Card4, { "data-testid": "questionnaire-card", children: [
1822
- questionnaire.title ? /* @__PURE__ */ jsx5(Title3, { children: questionnaire.title }) : null,
1823
2141
  questionnaire.description ? /* @__PURE__ */ jsx5(Description, { children: questionnaire.description }) : null,
1824
- /* @__PURE__ */ jsx5(QuestionList, { children: questionnaire.questions.map((question) => /* @__PURE__ */ jsxs3(QuestionCard, { children: [
2142
+ /* @__PURE__ */ jsx5(QuestionList, { children: /* @__PURE__ */ jsxs3(QuestionCard, { children: [
1825
2143
  /* @__PURE__ */ jsxs3(QuestionLabel, { children: [
1826
2144
  question.label,
1827
2145
  question.required ? /* @__PURE__ */ jsx5(Required, { children: "*" }) : null
1828
2146
  ] }),
2147
+ question.kind === "multi_select" ? /* @__PURE__ */ jsx5(QuestionHint, { children: resolvedLabels.multiSelectHint }) : null,
1829
2148
  renderQuestion(question)
1830
- ] }, question.id)) }),
2149
+ ] }, question.id) }),
1831
2150
  visibleErrorMessage ? /* @__PURE__ */ jsx5(ErrorMessage, { "data-testid": "questionnaire-error", children: visibleErrorMessage }) : null,
1832
2151
  isSubmitted ? /* @__PURE__ */ jsx5(SuccessMessage, { "data-testid": "questionnaire-success", children: resolvedLabels.submitted }) : interactive && !hasExternalFailureStatus ? /* @__PURE__ */ jsx5(
1833
2152
  SubmitButton,
@@ -1843,12 +2162,6 @@ var QuestionnaireCardInner = ({
1843
2162
  ) : null
1844
2163
  ] });
1845
2164
  };
1846
- var getQuestionnaireStateKey = (questionnaire) => JSON.stringify([
1847
- questionnaire.questionnaireId,
1848
- questionnaire.questions,
1849
- questionnaire.status,
1850
- questionnaire.statusMessage
1851
- ]);
1852
2165
  var QuestionnaireCard = (props) => /* @__PURE__ */ jsx5(QuestionnaireCardInner, { ...props }, getQuestionnaireStateKey(props.questionnaire));
1853
2166
  var Card4 = styled4.section`
1854
2167
  display: grid;
@@ -1858,11 +2171,6 @@ var Card4 = styled4.section`
1858
2171
  border: 1px solid rgba(255, 255, 255, 0.08);
1859
2172
  background: rgba(255, 255, 255, 0.03);
1860
2173
  `;
1861
- var Title3 = styled4.strong`
1862
- color: rgba(255, 255, 255, 0.94);
1863
- font-size: 16px;
1864
- line-height: 1.4;
1865
- `;
1866
2174
  var Description = styled4.p`
1867
2175
  margin: 0;
1868
2176
  color: rgba(255, 255, 255, 0.72);
@@ -1881,13 +2189,18 @@ var QuestionCard = styled4.div`
1881
2189
  `;
1882
2190
  var QuestionLabel = styled4.div`
1883
2191
  color: rgba(255, 255, 255, 0.9);
1884
- font-size: 13px;
2192
+ font-size: 14px;
1885
2193
  font-weight: 600;
1886
2194
  `;
1887
2195
  var Required = styled4.span`
1888
2196
  margin-left: 4px;
1889
2197
  color: rgba(255, 122, 122, 0.9);
1890
2198
  `;
2199
+ var QuestionHint = styled4.div`
2200
+ color: rgba(132, 180, 255, 0.9);
2201
+ font-size: 12px;
2202
+ line-height: 1.4;
2203
+ `;
1891
2204
  var QuestionBody = styled4.div`
1892
2205
  display: grid;
1893
2206
  gap: 10px;
@@ -1905,7 +2218,7 @@ var OptionChoiceItem = styled4.div`
1905
2218
  border: 1px solid rgba(255, 255, 255, 0.1);
1906
2219
  border-radius: 14px;
1907
2220
  background: rgba(255, 255, 255, 0.03);
1908
- padding: 12px 14px;
2221
+ padding: 2px 12px;
1909
2222
  color: rgba(255, 255, 255, 0.9);
1910
2223
  cursor: pointer;
1911
2224
  transition:
@@ -1956,8 +2269,11 @@ var OptionChoiceMarker = styled4.span`
1956
2269
  }
1957
2270
  `;
1958
2271
  var OptionChoiceContent = styled4.span`
1959
- display: grid;
1960
- gap: 2px;
2272
+ display: flex;
2273
+ flex-direction: column;
2274
+ justify-content: center;
2275
+ gap: 4px;
2276
+ min-height: 40px;
1961
2277
  min-width: 0;
1962
2278
  flex: 1;
1963
2279
  `;
@@ -1980,8 +2296,37 @@ var TextInput = styled4.input`
1980
2296
  color: rgba(255, 255, 255, 0.34);
1981
2297
  }
1982
2298
  `;
1983
- var InlineOtherInput = styled4(TextInput)`
2299
+ var InlineOtherInput = styled4(InputField)`
2300
+ width: 100%;
1984
2301
  margin-top: 0;
2302
+
2303
+ .compass-input-field-wrapper {
2304
+ min-height: 30px;
2305
+ border: 1px solid rgba(255, 255, 255, 0.1);
2306
+ border-radius: 10px;
2307
+ background: rgba(13, 15, 21, 0.55);
2308
+ box-shadow: none;
2309
+ padding: 2px 9px;
2310
+ }
2311
+
2312
+ .compass-input-field-wrapper:hover {
2313
+ border-color: rgba(126, 160, 255, 0.28);
2314
+ }
2315
+
2316
+ .compass-input-field-wrapper:focus-within {
2317
+ border-color: rgba(126, 160, 255, 0.42);
2318
+ box-shadow: 0 0 0 1px rgba(126, 160, 255, 0.14);
2319
+ }
2320
+
2321
+ .compass-input-field-input {
2322
+ color: rgba(255, 255, 255, 0.92);
2323
+ font-size: 13px;
2324
+ line-height: 1.2;
2325
+ }
2326
+
2327
+ .compass-input-field-input::placeholder {
2328
+ color: rgba(255, 255, 255, 0.34);
2329
+ }
1985
2330
  `;
1986
2331
  var NumberInputRow = styled4.div`
1987
2332
  display: flex;
@@ -2067,7 +2412,7 @@ var Detail = styled5.li`
2067
2412
 
2068
2413
  // src/components/chat-thread/components/image-viewer.tsx
2069
2414
  import styled6 from "@emotion/styled";
2070
- import { useEffect as useEffect3, useRef as useRef3 } from "react";
2415
+ import { useEffect as useEffect4, useRef as useRef4 } from "react";
2071
2416
  import { jsx as jsx7 } from "@emotion/react/jsx-runtime";
2072
2417
  var Overlay = styled6.div`
2073
2418
  position: fixed;
@@ -2086,8 +2431,8 @@ var Img = styled6.img`
2086
2431
  border-radius: 4px;
2087
2432
  `;
2088
2433
  var ImageViewer = ({ src, alt, onClose }) => {
2089
- const overlayRef = useRef3(null);
2090
- useEffect3(() => {
2434
+ const overlayRef = useRef4(null);
2435
+ useEffect4(() => {
2091
2436
  const handleKey = (e) => {
2092
2437
  if (e.key === "Escape")
2093
2438
  onClose();
@@ -2095,7 +2440,7 @@ var ImageViewer = ({ src, alt, onClose }) => {
2095
2440
  document.addEventListener("keydown", handleKey);
2096
2441
  return () => document.removeEventListener("keydown", handleKey);
2097
2442
  }, [onClose]);
2098
- useEffect3(() => {
2443
+ useEffect4(() => {
2099
2444
  overlayRef.current?.focus();
2100
2445
  }, []);
2101
2446
  const stopPropagation = (e) => e.stopPropagation();
@@ -2290,9 +2635,7 @@ var arePlanQuestionsEqual = (previousQuestion, nextQuestion) => {
2290
2635
  return false;
2291
2636
  }
2292
2637
  };
2293
- var areQuestionnairesEqual = (previousQuestionnaire, nextQuestionnaire) => previousQuestionnaire.questionnaireId === nextQuestionnaire.questionnaireId && previousQuestionnaire.title === nextQuestionnaire.title && previousQuestionnaire.description === nextQuestionnaire.description && previousQuestionnaire.submitLabel === nextQuestionnaire.submitLabel && previousQuestionnaire.status === nextQuestionnaire.status && previousQuestionnaire.statusMessage === nextQuestionnaire.statusMessage && previousQuestionnaire.questions.length === nextQuestionnaire.questions.length && previousQuestionnaire.questions.every(
2294
- (question, index) => arePlanQuestionsEqual(question, nextQuestionnaire.questions[index])
2295
- ) && areQuestionAnswerMapsEqual(previousQuestionnaire.answers, nextQuestionnaire.answers);
2638
+ var areQuestionnairesEqual = (previousQuestionnaire, nextQuestionnaire) => previousQuestionnaire.questionnaireId === nextQuestionnaire.questionnaireId && previousQuestionnaire.title === nextQuestionnaire.title && previousQuestionnaire.description === nextQuestionnaire.description && previousQuestionnaire.submitLabel === nextQuestionnaire.submitLabel && previousQuestionnaire.status === nextQuestionnaire.status && previousQuestionnaire.statusMessage === nextQuestionnaire.statusMessage && arePlanQuestionsEqual(previousQuestionnaire.question, nextQuestionnaire.question) && areQuestionAnswerMapsEqual(previousQuestionnaire.answers, nextQuestionnaire.answers);
2296
2639
  var areMessageBlocksEqual = (previousBlocks, nextBlocks) => {
2297
2640
  if (previousBlocks === nextBlocks) {
2298
2641
  return true;
@@ -2446,7 +2789,10 @@ var ChatMessageItemView = ({
2446
2789
  submitting: labels.questionnaireSubmitting,
2447
2790
  submitted: labels.questionnaireSubmitted,
2448
2791
  validationPrefix: labels.questionnaireValidationPrefix,
2449
- submitFailed: labels.questionnaireSubmitFailed
2792
+ submitFailed: labels.questionnaireSubmitFailed,
2793
+ multiSelectHint: labels.questionnaireMultiSelectHint,
2794
+ otherOptionLabel: labels.questionnaireOtherOptionLabel,
2795
+ otherPlaceholder: labels.questionnaireOtherPlaceholder
2450
2796
  },
2451
2797
  onSubmit: canSubmitQuestionnaire ? (submission) => onQuestionnaireSubmit({
2452
2798
  ...submission,
@@ -2705,8 +3051,34 @@ var CollapseToggle = styled7.button`
2705
3051
  `;
2706
3052
  var Content = styled7.div`
2707
3053
  color: rgba(255, 255, 255, 0.92);
3054
+ font-size: 14px;
2708
3055
  line-height: 1.6;
2709
3056
 
3057
+ h1,
3058
+ h2,
3059
+ h3,
3060
+ h4,
3061
+ h5,
3062
+ h6 {
3063
+ margin: 0;
3064
+ line-height: 2.5;
3065
+ }
3066
+
3067
+ h1 {
3068
+ font-size: 18px;
3069
+ }
3070
+
3071
+ h2 {
3072
+ font-size: 16px;
3073
+ }
3074
+
3075
+ h3,
3076
+ h4,
3077
+ h5,
3078
+ h6 {
3079
+ font-size: 14px;
3080
+ }
3081
+
2710
3082
  p {
2711
3083
  margin: 0;
2712
3084
  }
@@ -2718,7 +3090,7 @@ var Content = styled7.div`
2718
3090
  table {
2719
3091
  width: 100%;
2720
3092
  border-collapse: collapse;
2721
- margin: 0;
3093
+ margin: 8px 0 0;
2722
3094
  overflow: hidden;
2723
3095
  border-radius: 14px;
2724
3096
  border: 1px solid rgba(255, 255, 255, 0.08);
@@ -2742,6 +3114,10 @@ var Content = styled7.div`
2742
3114
  tbody tr:last-of-type td {
2743
3115
  border-bottom: none;
2744
3116
  }
3117
+ ul,
3118
+ ol {
3119
+ margin: 0 0 8px;
3120
+ }
2745
3121
  `;
2746
3122
  var ContentStack = styled7.div`
2747
3123
  display: flex;
@@ -2942,6 +3318,8 @@ var HeroSubtitle = styled8.p`
2942
3318
 
2943
3319
  // src/components/chat-thread/index.tsx
2944
3320
  import { jsx as jsx10, jsxs as jsxs7 } from "@emotion/react/jsx-runtime";
3321
+ var CHAT_THREAD_PINNED_THRESHOLD_PX = 32;
3322
+ var isThreadPinnedToBottom = (container) => container.scrollHeight - container.clientHeight - container.scrollTop <= CHAT_THREAD_PINNED_THRESHOLD_PX;
2945
3323
  var renderChatMessage = ({
2946
3324
  message,
2947
3325
  mode,
@@ -3015,12 +3393,13 @@ var ChatThreadView = ({
3015
3393
  streamingMessage,
3016
3394
  error,
3017
3395
  retryButtonLabel,
3396
+ scrollToLatestLabel,
3018
3397
  onRetry,
3019
3398
  onConfirmationSubmit,
3020
3399
  onQuestionnaireSubmit,
3021
3400
  renderMessageBlock
3022
3401
  }) => {
3023
- const containerRef = useRef4(null);
3402
+ const containerRef = useRef5(null);
3024
3403
  const conversationTurns = useMemo4(
3025
3404
  () => groupConversationTurns(historyMessages, streamingMessage),
3026
3405
  [historyMessages, streamingMessage]
@@ -3028,9 +3407,15 @@ var ChatThreadView = ({
3028
3407
  const latestTurn = conversationTurns[conversationTurns.length - 1];
3029
3408
  const previousTurns = conversationTurns.slice(0, -1);
3030
3409
  const latestUserMessageId = latestTurn?.userMessage?.id;
3031
- const latestUserMessageRef = useRef4(null);
3032
- const reservedSpaceFrameRef = useRef4(null);
3410
+ const latestHistoryMessage = historyMessages[historyMessages.length - 1];
3411
+ const latestTurnRef = useRef5(null);
3412
+ const reservedSpaceFrameRef = useRef5(null);
3413
+ const isPinnedRef = useRef5(true);
3414
+ const lastHistoryMessageIdRef = useRef5(latestHistoryMessage?.id);
3415
+ const lastStreamingMessageIdRef = useRef5(streamingMessage?.id);
3033
3416
  const [latestTurnMinHeight, setLatestTurnMinHeight] = useState4(0);
3417
+ const [isDetached, setIsDetached] = useState4(false);
3418
+ const [pendingNewMessageCount, setPendingNewMessageCount] = useState4(0);
3034
3419
  const measureLatestTurnMinHeight = useCallback3(() => {
3035
3420
  const container = containerRef.current;
3036
3421
  if (!container)
@@ -3041,26 +3426,86 @@ var ChatThreadView = ({
3041
3426
  const nextMinHeight = Math.max(0, container.clientHeight - paddingTop - paddingBottom);
3042
3427
  setLatestTurnMinHeight((current) => current === nextMinHeight ? current : nextMinHeight);
3043
3428
  }, []);
3044
- const scrollLatestUserMessageToTop = useCallback3(() => {
3429
+ const scrollToBottom = useCallback3((force = false) => {
3045
3430
  const container = containerRef.current;
3046
- const target = latestUserMessageRef.current;
3047
- if (!container || !target)
3048
- return;
3049
- const containerRect = container.getBoundingClientRect();
3050
- const targetRect = target.getBoundingClientRect();
3051
- const nextScrollTop = Math.max(
3052
- 0,
3053
- container.scrollTop + (targetRect.top - containerRect.top) - CHAT_THREAD_SCROLL_TOP_GAP
3054
- );
3431
+ if (!container)
3432
+ return false;
3433
+ if (!force && !isPinnedRef.current)
3434
+ return false;
3435
+ const nextScrollTop = Math.max(0, container.scrollHeight - container.clientHeight);
3436
+ container.scrollTop = nextScrollTop;
3055
3437
  if (typeof container.scrollTo === "function") {
3056
3438
  container.scrollTo({
3057
3439
  top: nextScrollTop,
3058
3440
  behavior: "auto"
3059
3441
  });
3442
+ }
3443
+ return true;
3444
+ }, []);
3445
+ const markThreadPinned = useCallback3(() => {
3446
+ isPinnedRef.current = true;
3447
+ setIsDetached(false);
3448
+ setPendingNewMessageCount(0);
3449
+ }, []);
3450
+ const scrollToBottomAndPin = useCallback3(
3451
+ (force = false) => {
3452
+ const didScroll = scrollToBottom(force);
3453
+ if (!didScroll)
3454
+ return;
3455
+ markThreadPinned();
3456
+ },
3457
+ [markThreadPinned, scrollToBottom]
3458
+ );
3459
+ const handleContainerScroll = useCallback3(() => {
3460
+ const container = containerRef.current;
3461
+ if (!container)
3060
3462
  return;
3463
+ const nextPinned = isThreadPinnedToBottom(container);
3464
+ isPinnedRef.current = nextPinned;
3465
+ setIsDetached(!nextPinned);
3466
+ if (nextPinned) {
3467
+ setPendingNewMessageCount(0);
3061
3468
  }
3062
- container.scrollTop = nextScrollTop;
3063
3469
  }, []);
3470
+ useLayoutEffect2(() => {
3471
+ const nextHistoryMessageId = latestHistoryMessage?.id;
3472
+ if (lastHistoryMessageIdRef.current === nextHistoryMessageId) {
3473
+ return;
3474
+ }
3475
+ lastHistoryMessageIdRef.current = nextHistoryMessageId;
3476
+ if (!latestHistoryMessage) {
3477
+ return;
3478
+ }
3479
+ if (latestHistoryMessage.role === "user") {
3480
+ window.requestAnimationFrame(() => {
3481
+ if (!scrollToBottom(true)) {
3482
+ return;
3483
+ }
3484
+ markThreadPinned();
3485
+ });
3486
+ return;
3487
+ }
3488
+ if (!isPinnedRef.current && latestHistoryMessage.role === "assistant" && latestHistoryMessage.id !== lastStreamingMessageIdRef.current) {
3489
+ window.requestAnimationFrame(() => {
3490
+ setPendingNewMessageCount((current) => current + 1);
3491
+ });
3492
+ }
3493
+ }, [latestHistoryMessage, markThreadPinned, scrollToBottom]);
3494
+ useLayoutEffect2(() => {
3495
+ const nextStreamingMessageId = streamingMessage?.id;
3496
+ if (lastStreamingMessageIdRef.current === nextStreamingMessageId) {
3497
+ return;
3498
+ }
3499
+ lastStreamingMessageIdRef.current = nextStreamingMessageId;
3500
+ if (!streamingMessage || streamingMessage.role !== "assistant") {
3501
+ return;
3502
+ }
3503
+ if (!isPinnedRef.current) {
3504
+ window.requestAnimationFrame(() => {
3505
+ setPendingNewMessageCount((current) => current + 1);
3506
+ });
3507
+ }
3508
+ }, [streamingMessage]);
3064
3509
  useLayoutEffect2(() => {
3065
3510
  if (reservedSpaceFrameRef.current !== null) {
3066
3511
  window.cancelAnimationFrame(reservedSpaceFrameRef.current);
@@ -3070,6 +3515,7 @@ var ChatThreadView = ({
3070
3515
  reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
3071
3516
  reservedSpaceFrameRef.current = null;
3072
3517
  setLatestTurnMinHeight((current) => current === 0 ? current : 0);
3518
+ scrollToBottom();
3073
3519
  });
3074
3520
  return () => {
3075
3521
  if (reservedSpaceFrameRef.current !== null) {
@@ -3081,7 +3527,7 @@ var ChatThreadView = ({
3081
3527
  reservedSpaceFrameRef.current = window.requestAnimationFrame(() => {
3082
3528
  reservedSpaceFrameRef.current = null;
3083
3529
  measureLatestTurnMinHeight();
3084
- scrollLatestUserMessageToTop();
3530
+ scrollToBottom();
3085
3531
  });
3086
3532
  return () => {
3087
3533
  if (reservedSpaceFrameRef.current !== null) {
@@ -3089,13 +3535,18 @@ var ChatThreadView = ({
3089
3535
  reservedSpaceFrameRef.current = null;
3090
3536
  }
3091
3537
  };
3092
- }, [latestUserMessageId, measureLatestTurnMinHeight, scrollLatestUserMessageToTop]);
3538
+ }, [latestTurn, latestUserMessageId, error, measureLatestTurnMinHeight, scrollToBottom]);
3093
3539
  useLayoutEffect2(() => {
3094
- if (!latestUserMessageId)
3540
+ if (!latestTurn)
3095
3541
  return;
3096
3542
  const handleResize = () => {
3543
+ if (!latestUserMessageId) {
3544
+ setLatestTurnMinHeight((current) => current === 0 ? current : 0);
3545
+ scrollToBottom();
3546
+ return;
3547
+ }
3097
3548
  measureLatestTurnMinHeight();
3098
- scrollLatestUserMessageToTop();
3549
+ scrollToBottom();
3099
3550
  };
3100
3551
  const container = containerRef.current;
3101
3552
  let resizeObserver = null;
@@ -3110,57 +3561,101 @@ var ChatThreadView = ({
3110
3561
  resizeObserver?.disconnect();
3111
3562
  window.removeEventListener("resize", handleResize);
3112
3563
  };
3113
- }, [latestUserMessageId, measureLatestTurnMinHeight, scrollLatestUserMessageToTop]);
3114
- return /* @__PURE__ */ jsxs7(Container, { ref: containerRef, "data-testid": "chat-thread", children: [
3115
- previousTurns.map((turn) => /* @__PURE__ */ jsxs7(ConversationTurn, { "data-testid": "chat-thread-turn", children: [
3116
- turn.userMessage ? /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
3117
- message: turn.userMessage,
3118
- mode: activeSessionMode,
3119
- onConfirmationSubmit,
3120
- onQuestionnaireSubmit,
3121
- renderMessageBlock
3122
- }) }) : null,
3123
- turn.responseMessages.map((message) => /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
3124
- message,
3125
- mode: activeSessionMode,
3126
- onConfirmationSubmit,
3127
- onQuestionnaireSubmit,
3128
- renderMessageBlock
3129
- }) }, message.id))
3130
- ] }, turn.id)),
3131
- latestTurn ? /* @__PURE__ */ jsxs7(
3132
- ConversationTurn,
3564
+ }, [latestTurn, latestUserMessageId, measureLatestTurnMinHeight, scrollToBottom]);
3565
+ useLayoutEffect2(() => {
3566
+ const latestTurnElement = latestTurnRef.current;
3567
+ if (!latestTurnElement || typeof ResizeObserver === "undefined") {
3568
+ return;
3569
+ }
3570
+ const observer = new ResizeObserver(() => {
3571
+ scrollToBottom();
3572
+ });
3573
+ observer.observe(latestTurnElement);
3574
+ return () => {
3575
+ observer.disconnect();
3576
+ };
3577
+ }, [latestTurn, scrollToBottom]);
3578
+ useLayoutEffect2(() => {
3579
+ const latestTurnElement = latestTurnRef.current;
3580
+ if (!latestTurnElement || typeof MutationObserver === "undefined") {
3581
+ return;
3582
+ }
3583
+ const observer = new MutationObserver(() => {
3584
+ scrollToBottom();
3585
+ });
3586
+ observer.observe(latestTurnElement, {
3587
+ childList: true,
3588
+ subtree: true,
3589
+ characterData: true
3590
+ });
3591
+ return () => {
3592
+ observer.disconnect();
3593
+ };
3594
+ }, [latestTurn, scrollToBottom]);
3595
+ return /* @__PURE__ */ jsxs7(ThreadViewport, { children: [
3596
+ /* @__PURE__ */ jsxs7(Container, { ref: containerRef, "data-testid": "chat-thread", onScroll: handleContainerScroll, children: [
3597
+ previousTurns.map((turn) => /* @__PURE__ */ jsxs7(ConversationTurn, { "data-testid": "chat-thread-turn", children: [
3598
+ turn.userMessage ? /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
3599
+ message: turn.userMessage,
3600
+ mode: activeSessionMode,
3601
+ onConfirmationSubmit,
3602
+ onQuestionnaireSubmit,
3603
+ renderMessageBlock
3604
+ }) }) : null,
3605
+ turn.responseMessages.map((message) => /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
3606
+ message,
3607
+ mode: activeSessionMode,
3608
+ onConfirmationSubmit,
3609
+ onQuestionnaireSubmit,
3610
+ renderMessageBlock
3611
+ }) }, message.id))
3612
+ ] }, turn.id)),
3613
+ latestTurn ? /* @__PURE__ */ jsxs7(
3614
+ ConversationTurn,
3615
+ {
3616
+ ref: latestTurnRef,
3617
+ "data-testid": "chat-thread-latest-turn",
3618
+ style: latestTurnMinHeight > 0 ? { minHeight: `${latestTurnMinHeight}px` } : void 0,
3619
+ children: [
3620
+ latestTurn.userMessage ? /* @__PURE__ */ jsx10(
3621
+ MessageSlot,
3622
+ {
3623
+ "data-testid": "chat-latest-user-anchor",
3624
+ style: { scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px` },
3625
+ children: renderChatMessage({
3626
+ message: latestTurn.userMessage,
3627
+ mode: activeSessionMode,
3628
+ onConfirmationSubmit,
3629
+ onQuestionnaireSubmit,
3630
+ renderMessageBlock
3631
+ })
3632
+ }
3633
+ ) : null,
3634
+ latestTurn.responseMessages.map((message) => /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
3635
+ message,
3636
+ mode: activeSessionMode,
3637
+ onConfirmationSubmit,
3638
+ onQuestionnaireSubmit,
3639
+ renderMessageBlock
3640
+ }) }, message.id)),
3641
+ error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
3642
+ ]
3643
+ }
3644
+ ) : null,
3645
+ !latestTurn && error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
3646
+ ] }),
3647
+ isDetached ? /* @__PURE__ */ jsx10(ScrollToLatestOverlay, { children: /* @__PURE__ */ jsxs7(
3648
+ ScrollToLatestButton,
3133
3649
  {
3134
- "data-testid": "chat-thread-latest-turn",
3135
- style: latestTurnMinHeight > 0 ? { minHeight: `${latestTurnMinHeight}px` } : void 0,
3650
+ type: "button",
3651
+ "data-testid": "chat-thread-scroll-to-latest",
3652
+ onClick: () => scrollToBottomAndPin(true),
3136
3653
  children: [
3137
- latestTurn.userMessage ? /* @__PURE__ */ jsx10(
3138
- MessageSlot,
3139
- {
3140
- ref: latestUserMessageRef,
3141
- "data-testid": "chat-latest-user-anchor",
3142
- style: { scrollMarginTop: `${CHAT_THREAD_SCROLL_TOP_GAP}px` },
3143
- children: renderChatMessage({
3144
- message: latestTurn.userMessage,
3145
- mode: activeSessionMode,
3146
- onConfirmationSubmit,
3147
- onQuestionnaireSubmit,
3148
- renderMessageBlock
3149
- })
3150
- }
3151
- ) : null,
3152
- latestTurn.responseMessages.map((message) => /* @__PURE__ */ jsx10(MessageSlot, { children: renderChatMessage({
3153
- message,
3154
- mode: activeSessionMode,
3155
- onConfirmationSubmit,
3156
- onQuestionnaireSubmit,
3157
- renderMessageBlock
3158
- }) }, message.id)),
3159
- error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
3654
+ scrollToLatestLabel,
3655
+ pendingNewMessageCount > 0 ? /* @__PURE__ */ jsx10(ScrollToLatestBadge, { "data-testid": "chat-thread-scroll-to-latest-count", children: pendingNewMessageCount }) : null
3160
3656
  ]
3161
3657
  }
3162
- ) : null,
3163
- !latestTurn && error ? renderErrorState({ error, onRetry, retryButtonLabel }) : null
3658
+ ) }) : null
3164
3659
  ] });
3165
3660
  };
3166
3661
  var EMPTY_MESSAGES = [];
@@ -3248,13 +3743,21 @@ var ChatThread = () => {
3248
3743
  streamingMessage,
3249
3744
  error,
3250
3745
  retryButtonLabel: labels.retryButton,
3746
+ scrollToLatestLabel: labels.scrollToLatest,
3251
3747
  onRetry: handleRetry,
3252
3748
  onConfirmationSubmit: handleConfirmation,
3253
3749
  onQuestionnaireSubmit: handleQuestionnaireSubmit,
3254
3750
  renderMessageBlock
3255
- }
3751
+ },
3752
+ activeSessionId ?? "chat-thread-empty"
3256
3753
  );
3257
3754
  };
3755
+ var ThreadViewport = styled9.div`
3756
+ position: relative;
3757
+ display: flex;
3758
+ flex: 1;
3759
+ min-height: 0;
3760
+ `;
3258
3761
  var Container = styled9.div`
3259
3762
  display: flex;
3260
3763
  flex: 1;
@@ -3262,8 +3765,7 @@ var Container = styled9.div`
3262
3765
  gap: 18px;
3263
3766
  min-height: 0;
3264
3767
  overflow: auto;
3265
- padding: 24px;
3266
- margin-bottom: 24px;
3768
+ padding: 24px 24px 88px;
3267
3769
  overscroll-behavior: contain;
3268
3770
 
3269
3771
  &::-webkit-scrollbar {
@@ -3315,9 +3817,51 @@ var RetryButton = styled9.button`
3315
3817
  background: rgba(255, 255, 255, 0.08);
3316
3818
  }
3317
3819
  `;
3820
+ var ScrollToLatestOverlay = styled9.div`
3821
+ position: absolute;
3822
+ right: 24px;
3823
+ bottom: 24px;
3824
+ left: 24px;
3825
+ display: flex;
3826
+ justify-content: center;
3827
+ pointer-events: none;
3828
+ `;
3829
+ var ScrollToLatestButton = styled9.button`
3830
+ display: inline-flex;
3831
+ align-items: center;
3832
+ gap: 8px;
3833
+ border: 1px solid rgba(255, 255, 255, 0.14);
3834
+ border-radius: 999px;
3835
+ background: rgba(17, 18, 21, 0.92);
3836
+ color: rgba(255, 255, 255, 0.9);
3837
+ font-size: 12px;
3838
+ line-height: 1;
3839
+ padding: 10px 14px;
3840
+ box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
3841
+ cursor: pointer;
3842
+ z-index: 1;
3843
+ pointer-events: auto;
3844
+
3845
+ &:hover {
3846
+ background: rgba(28, 30, 36, 0.96);
3847
+ }
3848
+ `;
3849
+ var ScrollToLatestBadge = styled9.span`
3850
+ display: inline-flex;
3851
+ min-width: 18px;
3852
+ height: 18px;
3853
+ align-items: center;
3854
+ justify-content: center;
3855
+ padding: 0 6px;
3856
+ border-radius: 999px;
3857
+ background: rgba(109, 170, 255, 0.2);
3858
+ color: #9ac0ff;
3859
+ font-size: 11px;
3860
+ font-weight: 600;
3861
+ `;
3318
3862
 
3319
3863
  // src/components/chat-composer/index.tsx
3320
- import { useEffect as useEffect6, useLayoutEffect as useLayoutEffect3, useRef as useRef7, useState as useState8 } from "react";
3864
+ import { useEffect as useEffect7, useLayoutEffect as useLayoutEffect3, useRef as useRef8, useState as useState8 } from "react";
3321
3865
  import styled14 from "@emotion/styled";
3322
3866
 
3323
3867
  // src/components/chat-composer/lib/chat-composer.ts
@@ -3429,10 +3973,10 @@ var resolveSendSession = ({
3429
3973
  };
3430
3974
 
3431
3975
  // src/components/chat-composer/hooks/use-chat-composer.ts
3432
- import { useCallback as useCallback4, useEffect as useEffect5, useRef as useRef6, useState as useState6 } from "react";
3976
+ import { useCallback as useCallback4, useEffect as useEffect6, useRef as useRef7, useState as useState6 } from "react";
3433
3977
 
3434
3978
  // src/components/chat-composer/hooks/use-composer-attachments.ts
3435
- import { useEffect as useEffect4, useRef as useRef5, useState as useState5 } from "react";
3979
+ import { useEffect as useEffect5, useRef as useRef6, useState as useState5 } from "react";
3436
3980
  var SUPPORTED_IMAGE_MIME_TYPES = /* @__PURE__ */ new Set(["image/png", "image/jpeg", "image/webp"]);
3437
3981
  var MAX_COMPOSER_ATTACHMENTS = 10;
3438
3982
  var createObjectUrl = (file) => typeof URL !== "undefined" && typeof URL.createObjectURL === "function" ? URL.createObjectURL(file) : "";
@@ -3447,11 +3991,11 @@ var releaseComposerAttachments = (attachments) => {
3447
3991
  };
3448
3992
  var useComposerAttachments = () => {
3449
3993
  const [attachments, setAttachments] = useState5([]);
3450
- const attachmentsRef = useRef5([]);
3451
- useEffect4(() => {
3994
+ const attachmentsRef = useRef6([]);
3995
+ useEffect5(() => {
3452
3996
  attachmentsRef.current = attachments;
3453
3997
  }, [attachments]);
3454
- useEffect4(
3998
+ useEffect5(
3455
3999
  () => () => {
3456
4000
  releaseComposerAttachments(attachmentsRef.current);
3457
4001
  },
@@ -3579,7 +4123,7 @@ var useChatComposer = () => {
3579
4123
  setIsModelsLoading(false);
3580
4124
  }
3581
4125
  }, [transport]);
3582
- useEffect5(() => {
4126
+ useEffect6(() => {
3583
4127
  void fetchModels();
3584
4128
  }, [fetchModels]);
3585
4129
  const hasModels = availableModels.length > 0;
@@ -3588,22 +4132,22 @@ var useChatComposer = () => {
3588
4132
  const [selectedMode, setSelectedModeLocal] = useState6(DEFAULT_CHAT_AGENT_MODE);
3589
4133
  const [attachmentNotice, setAttachmentNotice] = useState6(null);
3590
4134
  const { attachments, appendFiles, removeAttachment, takeMessageAttachments } = useComposerAttachments();
3591
- const abortControllerRef = useRef6(null);
3592
- const stopRequestRef = useRef6(null);
3593
- const lastRequestRef = useRef6(null);
3594
- useEffect5(() => {
4135
+ const abortControllerRef = useRef7(null);
4136
+ const stopRequestRef = useRef7(null);
4137
+ const lastRequestRef = useRef7(null);
4138
+ useEffect6(() => {
3595
4139
  setSelectedModel(
3596
4140
  (current) => resolveSelectedChatModel({ currentModel: current, availableModels, isModelsLoading })
3597
4141
  );
3598
4142
  }, [availableModels, isModelsLoading]);
3599
- useEffect5(() => {
4143
+ useEffect6(() => {
3600
4144
  if (activeSession) {
3601
4145
  setSelectedModeLocal(activeSession.mode ?? DEFAULT_CHAT_AGENT_MODE);
3602
4146
  return;
3603
4147
  }
3604
4148
  setSelectedModeLocal(preferredMode ?? DEFAULT_CHAT_AGENT_MODE);
3605
4149
  }, [activeSession, preferredMode]);
3606
- useEffect5(() => {
4150
+ useEffect6(() => {
3607
4151
  if (!attachmentNotice)
3608
4152
  return;
3609
4153
  const timeoutId = window.setTimeout(
@@ -4434,8 +4978,8 @@ var ChatComposerView = ({
4434
4978
  onStop,
4435
4979
  onSend
4436
4980
  }) => {
4437
- const imageInputRef = useRef7(null);
4438
- const inputRef = useRef7(null);
4981
+ const imageInputRef = useRef8(null);
4982
+ const inputRef = useRef8(null);
4439
4983
  const [isComposerExpandable, setIsComposerExpandable] = useState8(false);
4440
4984
  const [isComposerExpanded, setIsComposerExpanded] = useState8(false);
4441
4985
  const canSend = canSendChatMessage({
@@ -4586,7 +5130,7 @@ var ChatComposer = () => {
4586
5130
  const { labels, sendRef, retryRef, enableImageAttachments } = useChatContext();
4587
5131
  const { state, actions } = useChatComposer();
4588
5132
  const { send, retry } = actions;
4589
- useEffect6(() => {
5133
+ useEffect7(() => {
4590
5134
  sendRef.current = send;
4591
5135
  retryRef.current = async () => {
4592
5136
  retry();
@@ -4860,7 +5404,7 @@ var ChatConversationList = () => {
4860
5404
  };
4861
5405
  return /* @__PURE__ */ jsxs12(Container3, { children: [
4862
5406
  /* @__PURE__ */ jsxs12(Toolbar, { children: [
4863
- /* @__PURE__ */ jsx17(Title4, { children: "Sessions" }),
5407
+ /* @__PURE__ */ jsx17(Title3, { children: "Sessions" }),
4864
5408
  /* @__PURE__ */ jsx17(CreateButton, { type: "button", "data-testid": "chat-create-session", onClick: handleCreateSession, children: labels.newChat })
4865
5409
  ] }),
4866
5410
  /* @__PURE__ */ jsx17(List2, { "data-testid": "chat-session-list", children: sessions.map((session) => /* @__PURE__ */ jsx17(
@@ -4889,7 +5433,7 @@ var Toolbar = styled16.div`
4889
5433
  flex-direction: column;
4890
5434
  gap: 12px;
4891
5435
  `;
4892
- var Title4 = styled16.h2`
5436
+ var Title3 = styled16.h2`
4893
5437
  margin: 0;
4894
5438
  font-size: 14px;
4895
5439
  color: var(--text-secondary);