@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.d.mts +18 -2
- package/dist/index.d.ts +18 -2
- package/dist/index.js +821 -281
- package/dist/index.mjs +840 -296
- package/package.json +2 -2
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
|
|
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 {
|
|
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 {
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
1533
|
-
|
|
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
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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 "
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
return
|
|
1585
|
-
|
|
1586
|
-
|
|
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
|
|
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
|
|
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((
|
|
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 =
|
|
1626
|
-
questionnaire
|
|
1627
|
-
|
|
1628
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
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(
|
|
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
|
-
|
|
2012
|
+
questionToRender.options.map((option, index) => {
|
|
1721
2013
|
const isSelected = singleSelectDraft.selectedValue === option.value;
|
|
1722
|
-
return
|
|
1723
|
-
|
|
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
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
onClick: (
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
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
|
-
|
|
1760
|
-
|
|
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-${
|
|
2076
|
+
"data-testid": `question-input-${questionToRender.id}`,
|
|
1768
2077
|
type: "text",
|
|
1769
|
-
value: getTextInputValue(answers[
|
|
1770
|
-
placeholder:
|
|
2078
|
+
value: getTextInputValue(answers[questionToRender.id]),
|
|
2079
|
+
placeholder: questionToRender.placeholder,
|
|
1771
2080
|
readOnly: isInteractionLocked,
|
|
1772
2081
|
onChange: (event) => {
|
|
1773
|
-
setAnswers(
|
|
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-${
|
|
2093
|
+
"data-testid": `question-input-${questionToRender.id}`,
|
|
1783
2094
|
type: "number",
|
|
1784
|
-
value: getNumberInputValue(answers[
|
|
1785
|
-
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
1960
|
-
|
|
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(
|
|
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
|
|
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 =
|
|
2090
|
-
|
|
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
|
-
|
|
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.
|
|
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 =
|
|
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
|
|
3032
|
-
const
|
|
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
|
|
3429
|
+
const scrollToBottom = useCallback3((force = false) => {
|
|
3045
3430
|
const container = containerRef.current;
|
|
3046
|
-
|
|
3047
|
-
|
|
3048
|
-
|
|
3049
|
-
|
|
3050
|
-
const
|
|
3051
|
-
|
|
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
|
-
|
|
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,
|
|
3538
|
+
}, [latestTurn, latestUserMessageId, error, measureLatestTurnMinHeight, scrollToBottom]);
|
|
3093
3539
|
useLayoutEffect2(() => {
|
|
3094
|
-
if (!
|
|
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
|
-
|
|
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,
|
|
3114
|
-
|
|
3115
|
-
|
|
3116
|
-
|
|
3117
|
-
|
|
3118
|
-
|
|
3119
|
-
|
|
3120
|
-
|
|
3121
|
-
|
|
3122
|
-
|
|
3123
|
-
|
|
3124
|
-
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
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
|
-
|
|
3135
|
-
|
|
3650
|
+
type: "button",
|
|
3651
|
+
"data-testid": "chat-thread-scroll-to-latest",
|
|
3652
|
+
onClick: () => scrollToBottomAndPin(true),
|
|
3136
3653
|
children: [
|
|
3137
|
-
|
|
3138
|
-
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
3451
|
-
|
|
3994
|
+
const attachmentsRef = useRef6([]);
|
|
3995
|
+
useEffect5(() => {
|
|
3452
3996
|
attachmentsRef.current = attachments;
|
|
3453
3997
|
}, [attachments]);
|
|
3454
|
-
|
|
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
|
-
|
|
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 =
|
|
3592
|
-
const stopRequestRef =
|
|
3593
|
-
const lastRequestRef =
|
|
3594
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
4438
|
-
const inputRef =
|
|
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
|
-
|
|
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(
|
|
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
|
|
5436
|
+
var Title3 = styled16.h2`
|
|
4893
5437
|
margin: 0;
|
|
4894
5438
|
font-size: 14px;
|
|
4895
5439
|
color: var(--text-secondary);
|