synapse-react-client 4.0.34 → 4.0.35

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/SWC.index.js +1 -1
  2. package/dist/assets/icons/curie-head.svg +21 -0
  3. package/dist/components/AccessRequirementList/AccessRequirementList.d.ts +24 -2
  4. package/dist/components/AccessRequirementList/AccessRequirementList.d.ts.map +1 -1
  5. package/dist/components/AccessRequirementList/AccessRequirementList.js +108 -106
  6. package/dist/components/AccessRequirementList/AccessRequirementList.js.map +1 -1
  7. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.d.ts.map +1 -1
  8. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.js +26 -25
  9. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/EDucPreviewStep/EDucPreviewStep.js.map +1 -1
  10. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.d.ts.map +1 -1
  11. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.js +1 -1
  12. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/ManualUploadDucStep/ManualUploadDucStep.js.map +1 -1
  13. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/SignatureStatusStep/SignatureStatusStep.d.ts +6 -1
  14. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/SignatureStatusStep/SignatureStatusStep.d.ts.map +1 -1
  15. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/SignatureStatusStep/SignatureStatusStep.js +55 -54
  16. package/dist/components/AccessRequirementList/ManagedACTAccessRequirementRequestFlow/SignatureStatusStep/SignatureStatusStep.js.map +1 -1
  17. package/dist/components/DraggableDialog/DraggableDialog.d.ts +2 -1
  18. package/dist/components/DraggableDialog/DraggableDialog.d.ts.map +1 -1
  19. package/dist/components/DraggableDialog/DraggableDialog.js +42 -33
  20. package/dist/components/DraggableDialog/DraggableDialog.js.map +1 -1
  21. package/dist/components/EntityTreeTable/hooks/useTableColumns.d.ts.map +1 -1
  22. package/dist/components/EntityTreeTable/hooks/useTableColumns.js +12 -6
  23. package/dist/components/EntityTreeTable/hooks/useTableColumns.js.map +1 -1
  24. package/dist/components/ExperimentalMode/ExperimentalMode.js +2 -1
  25. package/dist/components/ExperimentalMode/ExperimentalMode.js.map +1 -1
  26. package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.d.ts.map +1 -1
  27. package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.js +64 -55
  28. package/dist/components/SearchQueryWrapper/SearchQueryUseQueryOptions.js.map +1 -1
  29. package/dist/components/StorybookComponentWrapper.d.ts.map +1 -1
  30. package/dist/components/StorybookComponentWrapper.js +15 -10
  31. package/dist/components/StorybookComponentWrapper.js.map +1 -1
  32. package/dist/components/SynapseChat/GridAgentChat.js +6 -6
  33. package/dist/components/SynapseChat/SynapseChat.d.ts +2 -1
  34. package/dist/components/SynapseChat/SynapseChat.d.ts.map +1 -1
  35. package/dist/components/SynapseChat/SynapseChat.js +118 -74
  36. package/dist/components/SynapseChat/SynapseChat.js.map +1 -1
  37. package/dist/components/SynapseChat/SynapseChatInteraction.d.ts +3 -1
  38. package/dist/components/SynapseChat/SynapseChatInteraction.d.ts.map +1 -1
  39. package/dist/components/SynapseChat/SynapseChatInteraction.js +21 -21
  40. package/dist/components/SynapseChat/SynapseChatInteraction.js.map +1 -1
  41. package/dist/components/SynapseChat/SynapseChatMessage.d.ts +1 -0
  42. package/dist/components/SynapseChat/SynapseChatMessage.d.ts.map +1 -1
  43. package/dist/components/SynapseChat/SynapseChatMessage.js +9 -8
  44. package/dist/components/SynapseChat/SynapseChatMessage.js.map +1 -1
  45. package/dist/components/SynapseChat/SynapsePortalChatDialog.d.ts.map +1 -1
  46. package/dist/components/SynapseChat/SynapsePortalChatDialog.js +2 -1
  47. package/dist/components/SynapseChat/SynapsePortalChatDialog.js.map +1 -1
  48. package/dist/components/TanStackTable/TanStackTableUtils.d.ts.map +1 -1
  49. package/dist/components/TanStackTable/TanStackTableUtils.js +6 -6
  50. package/dist/components/TanStackTable/TanStackTableUtils.js.map +1 -1
  51. package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.d.ts +12 -0
  52. package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.d.ts.map +1 -0
  53. package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.js +84 -0
  54. package/dist/components/dataaccess/UserAccessRequestHistory/InFlightEDucSignaturesTable.js.map +1 -0
  55. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryPage.d.ts.map +1 -1
  56. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryPage.js +20 -18
  57. package/dist/components/dataaccess/UserAccessRequestHistory/UserAccessRequestHistoryPage.js.map +1 -1
  58. package/dist/components/entity/page/CreatedByModifiedBy.d.ts.map +1 -1
  59. package/dist/components/entity/page/CreatedByModifiedBy.js +43 -61
  60. package/dist/components/entity/page/CreatedByModifiedBy.js.map +1 -1
  61. package/dist/components/entity/page/createdByModifiedByTooltips.d.ts +7 -0
  62. package/dist/components/entity/page/createdByModifiedByTooltips.d.ts.map +1 -0
  63. package/dist/components/entity/page/createdByModifiedByTooltips.js +36 -0
  64. package/dist/components/entity/page/createdByModifiedByTooltips.js.map +1 -0
  65. package/dist/components/entity/page/title_bar/useGetEntityTitleBarProperties.d.ts.map +1 -1
  66. package/dist/components/entity/page/title_bar/useGetEntityTitleBarProperties.js +130 -65
  67. package/dist/components/entity/page/title_bar/useGetEntityTitleBarProperties.js.map +1 -1
  68. package/dist/components/widgets/Range.js +8 -2
  69. package/dist/components/widgets/Range.js.map +1 -1
  70. package/dist/components/widgets/query-filter/RangeFacetFilter.d.ts.map +1 -1
  71. package/dist/components/widgets/query-filter/RangeFacetFilter.js +2 -1
  72. package/dist/components/widgets/query-filter/RangeFacetFilter.js.map +1 -1
  73. package/dist/components/widgets/query-filter/RangeFacetFilterUI.d.ts.map +1 -1
  74. package/dist/components/widgets/query-filter/RangeFacetFilterUI.js +19 -19
  75. package/dist/components/widgets/query-filter/RangeFacetFilterUI.js.map +1 -1
  76. package/dist/mocks/msw/handlers/fileHandlers.d.ts.map +1 -1
  77. package/dist/mocks/msw/handlers/fileHandlers.js +22 -22
  78. package/dist/mocks/msw/handlers/fileHandlers.js.map +1 -1
  79. package/dist/mocks/msw/handlers/userProfileHandlers.d.ts.map +1 -1
  80. package/dist/mocks/msw/handlers/userProfileHandlers.js +36 -36
  81. package/dist/mocks/msw/handlers/userProfileHandlers.js.map +1 -1
  82. package/dist/mocks/user/mock_user_profile.d.ts +1 -0
  83. package/dist/mocks/user/mock_user_profile.d.ts.map +1 -1
  84. package/dist/mocks/user/mock_user_profile.js +45 -45
  85. package/dist/mocks/user/mock_user_profile.js.map +1 -1
  86. package/dist/synapse-queries/KeyFactory.d.ts +1 -0
  87. package/dist/synapse-queries/KeyFactory.d.ts.map +1 -1
  88. package/dist/synapse-queries/KeyFactory.js +3 -0
  89. package/dist/synapse-queries/KeyFactory.js.map +1 -1
  90. package/dist/synapse-queries/dataaccess/index.js +4 -4
  91. package/dist/synapse-queries/dataaccess/useEDuc.d.ts +13 -1
  92. package/dist/synapse-queries/dataaccess/useEDuc.d.ts.map +1 -1
  93. package/dist/synapse-queries/dataaccess/useEDuc.js +67 -49
  94. package/dist/synapse-queries/dataaccess/useEDuc.js.map +1 -1
  95. package/dist/synapse-queries/index.js +185 -184
  96. package/dist/tsconfig.build.tsbuildinfo +1 -1
  97. package/dist/utils/APIConstants.d.ts +1 -0
  98. package/dist/utils/APIConstants.d.ts.map +1 -1
  99. package/dist/utils/APIConstants.js +2 -2
  100. package/dist/utils/APIConstants.js.map +1 -1
  101. package/dist/utils/AppUtils/session/ApplicationSessionManager.d.ts.map +1 -1
  102. package/dist/utils/AppUtils/session/ApplicationSessionManager.js +20 -19
  103. package/dist/utils/AppUtils/session/ApplicationSessionManager.js.map +1 -1
  104. package/dist/utils/featureflag/FeatureFlags.d.ts +1 -0
  105. package/dist/utils/featureflag/FeatureFlags.d.ts.map +1 -1
  106. package/dist/utils/featureflag/FeatureFlags.js +1 -1
  107. package/dist/utils/featureflag/FeatureFlags.js.map +1 -1
  108. package/dist/utils/functions/RegularExpressions.d.ts +7 -0
  109. package/dist/utils/functions/RegularExpressions.d.ts.map +1 -1
  110. package/dist/utils/functions/RegularExpressions.js +5 -4
  111. package/dist/utils/functions/RegularExpressions.js.map +1 -1
  112. package/dist/utils/hooks/useIsHydrated.d.ts +9 -0
  113. package/dist/utils/hooks/useIsHydrated.d.ts.map +1 -0
  114. package/dist/utils/hooks/useIsHydrated.js +10 -0
  115. package/dist/utils/hooks/useIsHydrated.js.map +1 -0
  116. package/package.json +2 -2
@@ -5,6 +5,7 @@ export type SynapseChatProps = {
5
5
  initialMessage?: string;
6
6
  agentRegistrationId?: string;
7
7
  chatbotName?: string;
8
+ variant?: 'default' | 'curie';
8
9
  hideTitle?: boolean;
9
10
  textboxPositionOffset?: string;
10
11
  sessionContext?: GridAgentSessionContext;
@@ -29,6 +30,6 @@ export type SynapseChatProps = {
29
30
  */
30
31
  allowAttachments?: boolean;
31
32
  };
32
- export declare function SynapseChat({ initialMessage, agentRegistrationId, chatbotName, agentAvatar, hideTitle, textboxPositionOffset, sessionContext, externalSession, setExternalSession, externalChatState, showAccessLevelMenu, onChatResponse, suggestedPrompts, allowAttachments, }: SynapseChatProps): import("react/jsx-runtime").JSX.Element;
33
+ export declare function SynapseChat({ initialMessage, agentRegistrationId, chatbotName, variant, agentAvatar, hideTitle, textboxPositionOffset, sessionContext, externalSession, setExternalSession, externalChatState, showAccessLevelMenu, onChatResponse, suggestedPrompts, allowAttachments, }: SynapseChatProps): import("react/jsx-runtime").JSX.Element;
33
34
  export default SynapseChat;
34
35
  //# sourceMappingURL=SynapseChat.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseChat.d.ts","sourceRoot":"","sources":["../../../src/components/SynapseChat/SynapseChat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,MAAM,uCAAuC,CAAA;AAO/E,OAAO,EACL,YAAY,EAEZ,uBAAuB,EACxB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AA4BlE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAE9B,cAAc,CAAC,EAAE,uBAAuB,CAAA;IAExC,eAAe,CAAC,EAAE,YAAY,CAAA;IAE9B,kBAAkB,CAAC,EAAE,CAAC,CAAC,EAAE,YAAY,GAAG,SAAS,KAAK,IAAI,CAAA;IAE1D,iBAAiB,CAAC,EAAE,SAAS,CAAA;IAE7B,uBAAuB,CAAC,EAAE,gBAAgB,CAAA;IAE1C,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,mDAAmD;IACnD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,wBAAgB,WAAW,CAAC,EAC1B,cAAc,EACd,mBAAmB,EACnB,WAA2B,EAC3B,WAA4B,EAC5B,SAAiB,EACjB,qBAA6B,EAC7B,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,mBAA0B,EAC1B,cAAc,EACd,gBAAgB,EAChB,gBAAwB,GACzB,EAAE,gBAAgB,2CAwQlB;AAED,eAAe,WAAW,CAAA"}
1
+ {"version":3,"file":"SynapseChat.d.ts","sourceRoot":"","sources":["../../../src/components/SynapseChat/SynapseChat.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAgB,MAAM,uCAAuC,CAAA;AAe/E,OAAO,EACL,YAAY,EAEZ,uBAAuB,EACxB,MAAM,kCAAkC,CAAA;AACzC,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAsDlE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,mBAAmB,CAAC,EAAE,MAAM,CAAA;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,SAAS,GAAG,OAAO,CAAA;IAC7B,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,qBAAqB,CAAC,EAAE,MAAM,CAAA;IAE9B,cAAc,CAAC,EAAE,uBAAuB,CAAA;IAExC,eAAe,CAAC,EAAE,YAAY,CAAA;IAE9B,kBAAkB,CAAC,EAAE,CAAC,CAAC,EAAE,YAAY,GAAG,SAAS,KAAK,IAAI,CAAA;IAE1D,iBAAiB,CAAC,EAAE,SAAS,CAAA;IAE7B,uBAAuB,CAAC,EAAE,gBAAgB,CAAA;IAE1C,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;OAIG;IACH,cAAc,CAAC,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,wFAAwF;IACxF,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAA;IAC3B,mDAAmD;IACnD,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;IAC7B;;;OAGG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B,CAAA;AAED,wBAAgB,WAAW,CAAC,EAC1B,cAAc,EACd,mBAAmB,EACnB,WAA2B,EAC3B,OAAmB,EACnB,WAAW,EACX,SAAiB,EACjB,qBAA6B,EAC7B,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,iBAAiB,EACjB,mBAA0B,EAC1B,cAAc,EACd,gBAAgB,EAChB,gBAAwB,GACzB,EAAE,gBAAgB,2CAmTlB;AAED,eAAe,WAAW,CAAA"}
@@ -6,20 +6,25 @@ import "../../utils/AppUtils/index.js";
6
6
  import "../../utils/index.js";
7
7
  import { SkeletonParagraph as ee } from "../Skeleton/SkeletonParagraph.js";
8
8
  import "../Skeleton/index.js";
9
- import { UserCard as te } from "../UserCard/UserCard.js";
10
- import { useCreateAgentSession as r, useUpdateAgentSession as ne } from "../../synapse-queries/chat/useChat.js";
11
- import { useChatState as i } from "./useChatState.js";
12
- import { AccessLevelMenu as a } from "./AccessLevelMenu.js";
13
- import { ChatInputArea as o } from "./components/ChatInputArea/ChatInputArea.js";
14
- import s from "./SynapseChatMessage.js";
9
+ import { UserCard as r } from "../UserCard/UserCard.js";
10
+ import { useCreateAgentSession as i, useUpdateAgentSession as a } from "../../synapse-queries/chat/useChat.js";
11
+ import { useChatState as o } from "./useChatState.js";
12
+ import { AccessLevelMenu as te } from "./AccessLevelMenu.js";
13
+ import { ChatInputArea as ne } from "./components/ChatInputArea/ChatInputArea.js";
14
+ import re from "./SynapseChatMessage.js";
15
+ import { ForwardRef as s } from "../../assets/illustrations/curie_avatar_head.js";
15
16
  import { useEffect as c, useRef as l, useState as u } from "react";
16
- import { Alert as d, Box as f, Chip as p, List as m, Stack as h, Typography as g } from "@mui/material";
17
- import { jsx as _, jsxs as v } from "react/jsx-runtime";
18
- import { AgentAccessLevel as y } from "@sage-bionetworks/synapse-types";
19
- import { FileHandleAssociateType as b } from "@sage-bionetworks/synapse-client";
17
+ import { Alert as d, Box as f, Chip as p, List as m, ListItem as h, Stack as g, Typography as _ } from "@mui/material";
18
+ import { jsx as v, jsxs as y } from "react/jsx-runtime";
19
+ import { AgentAccessLevel as b } from "@sage-bionetworks/synapse-types";
20
+ import { FileHandleAssociateType as ie } from "@sage-bionetworks/synapse-client";
20
21
  import { SmartToyTwoTone as x } from "@mui/icons-material";
21
22
  //#region src/components/SynapseChat/SynapseChat.tsx
22
- var S = /* @__PURE__ */ _(f, {
23
+ var ae = "Hi! How can I help you today?", oe = {
24
+ [b.PUBLICLY_ACCESSIBLE]: " and can only read public data.",
25
+ [b.READ_YOUR_PRIVATE_DATA]: " and can read your public and private data in Synapse on your behalf.",
26
+ [b.WRITE_YOUR_PRIVATE_DATA]: " and can read and write your public and private data in Synapse on your behalf."
27
+ }, se = /* @__PURE__ */ v(f, {
23
28
  sx: {
24
29
  p: "3px",
25
30
  borderRadius: "50%",
@@ -29,54 +34,73 @@ var S = /* @__PURE__ */ _(f, {
29
34
  mt: "10px",
30
35
  height: "31px"
31
36
  },
32
- children: /* @__PURE__ */ _(x, { sx: { color: "secondary.main" } })
37
+ children: /* @__PURE__ */ v(x, { sx: { color: "secondary.main" } })
38
+ }), S = /* @__PURE__ */ v(f, {
39
+ sx: {
40
+ mt: "10px",
41
+ height: "30px",
42
+ width: "30px",
43
+ display: "flex",
44
+ alignItems: "center",
45
+ justifyContent: "center"
46
+ },
47
+ children: /* @__PURE__ */ v(s, {})
33
48
  });
34
- function C({ initialMessage: x, agentRegistrationId: C, chatbotName: w = "SynapseChat", agentAvatar: T = S, hideTitle: E = !1, textboxPositionOffset: D = "0px", sessionContext: O, externalSession: k, setExternalSession: A, externalChatState: j, showAccessLevelMenu: M = !0, onChatResponse: N, suggestedPrompts: P, allowAttachments: F = !1 }) {
35
- let { accessToken: I } = t(), { userId: L } = n(), R = /* @__PURE__ */ _(te, {
49
+ function C({ initialMessage: s, agentRegistrationId: x, chatbotName: C = "SynapseChat", variant: w = "default", agentAvatar: T, hideTitle: E = !1, textboxPositionOffset: D = "0px", sessionContext: O, externalSession: k, setExternalSession: A, externalChatState: j, showAccessLevelMenu: M = !0, onChatResponse: N, suggestedPrompts: P, allowAttachments: F = !1 }) {
50
+ let { accessToken: I } = t(), { userId: L } = n(), R = C + " can make mistakes", z = T ?? (w === "curie" ? S : se), B = /* @__PURE__ */ v(r, {
36
51
  ownerId: L,
37
52
  size: "AVATAR",
38
53
  avatarSize: "MEDIUM"
39
- }), [z, re] = u(), B = k ?? z, V = A ?? re, { mutate: H, error: U } = r({ onSuccess: (e) => V(e) }), { mutate: W } = ne({
40
- onSuccess: (e) => V(e),
54
+ }), [V, ce] = u(), H = k ?? V, U = A ?? ce, { mutate: W, error: G } = i({ onSuccess: (e) => U(e) }), { mutate: le } = a({
55
+ onSuccess: (e) => U(e),
41
56
  onError: (t) => e(`Unable to update the agent session: ${t.message}`, "danger")
42
- }), [G, K] = u(O ? y.WRITE_YOUR_PRIVATE_DATA : y.PUBLICLY_ACCESSIBLE), q = i(B, N), { interactions: J, isAwaitingResponse: Y, sendChat: X } = j ?? q, Z = l(void 0);
43
- Z.current === void 0 && (Z.current = new Set(J.map((e) => e.id)));
44
- let ie = Z.current, [ae, Q] = u(""), [$, oe] = u(!1), [se, ce] = u([]);
45
- return c(() => {
46
- H && !B && H({
47
- agentAccessLevel: G,
48
- agentRegistrationId: C,
57
+ }), [K, ue] = u(O ? b.WRITE_YOUR_PRIVATE_DATA : b.PUBLICLY_ACCESSIBLE), de = o(H, N), { interactions: q, isAwaitingResponse: J, sendChat: Y } = j ?? de, X = w === "curie" && q.length === 0, fe = R + (w === "curie" ? oe[K] : ""), Z = l(void 0);
58
+ Z.current === void 0 && (Z.current = new Set(q.map((e) => e.id)));
59
+ let pe = Z.current, [me, Q] = u(""), [$, he] = u(!1), [ge, _e] = u([]);
60
+ c(() => {
61
+ W && !H && W({
62
+ agentAccessLevel: K,
63
+ agentRegistrationId: x,
49
64
  sessionContext: O
50
65
  });
51
66
  }, [
67
+ W,
52
68
  H,
53
- B,
54
69
  I,
55
70
  O,
56
- G,
57
- C
71
+ K,
72
+ x
58
73
  ]), c(() => {
59
- B && x && !$ && (X(x), oe(!0));
74
+ H && s && !$ && (Y(s), he(!0));
60
75
  }, [
61
- B,
62
- x,
76
+ H,
77
+ s,
63
78
  $,
64
- X
65
- ]), U ? /* @__PURE__ */ _(d, {
79
+ Y
80
+ ]);
81
+ let ve = (e, t) => {
82
+ Y(e, t.length ? t.map((e) => ({
83
+ fileHandleId: e.fileHandleId,
84
+ associateObjectId: e.fileHandleId,
85
+ associateObjectType: ie.FileEntity
86
+ })) : void 0), Q(""), _e(t);
87
+ };
88
+ return G ? /* @__PURE__ */ v(d, {
66
89
  severity: "error",
67
90
  sx: { my: 2 },
68
- children: U.reason
69
- }) : /* @__PURE__ */ v(f, {
91
+ children: G.reason
92
+ }) : /* @__PURE__ */ y(f, {
70
93
  sx: {
71
94
  display: "flex",
72
95
  flexDirection: "column",
73
- justifyContent: "space-between",
96
+ justifyContent: w === "curie" ? "flex-start" : "space-between",
97
+ gap: w === "curie" ? "20px" : "0",
74
98
  maxWidth: "1100px",
75
99
  mx: "auto",
76
100
  height: "100%"
77
101
  },
78
102
  children: [
79
- !E && /* @__PURE__ */ _(g, {
103
+ !E && /* @__PURE__ */ v(_, {
80
104
  variant: "headline1",
81
105
  sx: {
82
106
  p: "20px",
@@ -87,55 +111,81 @@ function C({ initialMessage: x, agentRegistrationId: C, chatbotName: w = "Synaps
87
111
  backgroundColor: "white",
88
112
  zIndex: 1
89
113
  },
90
- children: w
114
+ children: C
91
115
  }),
92
- M && /* @__PURE__ */ _(a, {
93
- initAccessLevel: G,
116
+ M && w !== "curie" && /* @__PURE__ */ v(te, {
117
+ initAccessLevel: K,
94
118
  onChange: (e) => {
95
- K(e), W({
119
+ ue(e), le({
96
120
  agentAccessLevel: e,
97
- sessionId: B.sessionId
121
+ sessionId: H.sessionId
98
122
  });
99
123
  }
100
124
  }),
101
- !B && /* @__PURE__ */ _(ee, { numRows: 10 }),
102
- B && /* @__PURE__ */ _(f, {
125
+ !H && /* @__PURE__ */ v(ee, { numRows: 10 }),
126
+ H && /* @__PURE__ */ v(f, {
103
127
  sx: {
104
128
  flexGrow: 1,
105
129
  overflowY: "auto",
106
- mb: 2
130
+ mb: w === "curie" ? 0 : 2
107
131
  },
108
- children: /* @__PURE__ */ _(m, {
132
+ children: /* @__PURE__ */ y(m, {
109
133
  sx: {
110
134
  flex: 1,
111
135
  overflowY: "auto",
112
136
  pt: "20px",
137
+ ...w === "curie" && {
138
+ pt: 0,
139
+ pb: 0,
140
+ gap: "20px"
141
+ },
113
142
  display: "flex",
114
- flexDirection: "column"
143
+ flexDirection: "column",
144
+ ...X && {
145
+ minHeight: "100%",
146
+ justifyContent: "flex-end"
147
+ }
115
148
  },
116
- children: J.map((e, t) => {
117
- let n = t === J.length - 1;
118
- return /* @__PURE__ */ _(s, {
119
- agentAvatar: T,
120
- userAvatar: R,
149
+ children: [X && /* @__PURE__ */ y(h, {
150
+ sx: {
151
+ display: "grid",
152
+ gridTemplateColumns: "50px auto",
153
+ columnGap: "16px",
154
+ justifyItems: "start",
155
+ alignItems: "start",
156
+ p: 0
157
+ },
158
+ children: [z, /* @__PURE__ */ v(_, {
159
+ sx: {
160
+ justifySelf: "start",
161
+ mt: "14px"
162
+ },
163
+ children: ae
164
+ })]
165
+ }), q.map((e, t) => {
166
+ let n = t === q.length - 1;
167
+ return /* @__PURE__ */ v(re, {
168
+ agentAvatar: z,
169
+ userAvatar: B,
121
170
  userMessage: e.userMessage,
122
171
  chatJobId: e.jobId,
123
- onSendChat: X,
172
+ showLoadingIcon: w !== "curie",
173
+ onSendChat: Y,
124
174
  scrollIntoView: n,
125
- animateEntry: !ie.has(e.id),
126
- isAwaitingResponse: Y,
127
- pendingAttachments: n ? se : void 0
175
+ animateEntry: !pe.has(e.id),
176
+ isAwaitingResponse: J,
177
+ pendingAttachments: n ? ge : void 0
128
178
  }, e.id);
129
- })
179
+ })]
130
180
  })
131
181
  }),
132
- /* @__PURE__ */ v(f, {
182
+ /* @__PURE__ */ y(f, {
133
183
  sx: {
134
184
  position: "sticky",
135
- bottom: D,
185
+ bottom: w === "curie" ? "0px" : D,
136
186
  backgroundColor: "white"
137
187
  },
138
- children: [P && P.length > 0 && J.length === 0 && /* @__PURE__ */ _(h, {
188
+ children: [P && P.length > 0 && q.length === 0 && /* @__PURE__ */ v(g, {
139
189
  direction: "row",
140
190
  spacing: 1,
141
191
  sx: {
@@ -143,38 +193,32 @@ function C({ initialMessage: x, agentRegistrationId: C, chatbotName: w = "Synaps
143
193
  flexWrap: "wrap",
144
194
  gap: 1
145
195
  },
146
- children: P.map((e) => /* @__PURE__ */ _(p, {
196
+ children: P.map((e) => /* @__PURE__ */ v(p, {
147
197
  label: e,
148
198
  variant: "outlined",
149
199
  onClick: () => Q(e),
150
- disabled: !B,
200
+ disabled: !H,
151
201
  sx: { cursor: "pointer" }
152
202
  }, e))
153
- }), /* @__PURE__ */ _(f, {
203
+ }), /* @__PURE__ */ v(f, {
154
204
  sx: { mt: "5px" },
155
- children: /* @__PURE__ */ _(o, {
156
- value: ae,
205
+ children: /* @__PURE__ */ v(ne, {
206
+ value: me,
157
207
  onValueChange: Q,
158
- onSend: (e, t) => {
159
- X(e, t.length ? t.map((e) => ({
160
- fileHandleId: e.fileHandleId,
161
- associateObjectId: e.fileHandleId,
162
- associateObjectType: b.FileEntity
163
- })) : void 0), Q(""), ce(t);
164
- },
165
- placeholder: `Message ${w}`,
166
- disabled: !B || Y,
208
+ onSend: ve,
209
+ placeholder: w === "curie" ? "Ask anything" : `Message ${C}`,
210
+ disabled: !H || J,
167
211
  allowAttachments: F
168
212
  })
169
213
  })]
170
214
  }),
171
- /* @__PURE__ */ v(g, {
215
+ /* @__PURE__ */ v(_, {
172
216
  variant: "smallText1",
173
217
  sx: {
174
218
  pt: "8px",
175
219
  textAlign: "center"
176
220
  },
177
- children: [w, " can make mistakes."]
221
+ children: fe
178
222
  })
179
223
  ]
180
224
  });
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseChat.js","names":[],"sources":["../../../src/components/SynapseChat/SynapseChat.tsx"],"sourcesContent":["import { ChatState, useChatState } from '@/components/SynapseChat/useChatState'\nimport {\n useCreateAgentSession,\n useUpdateAgentSession,\n} from '@/synapse-queries/chat/useChat'\nimport { useSynapseContext } from '@/utils'\nimport { Alert, Box, Chip, List, Stack, Typography } from '@mui/material'\nimport {\n AgentSession,\n FileHandleAssociateType,\n GridAgentSessionContext,\n} from '@sage-bionetworks/synapse-client'\nimport { AgentAccessLevel } from '@sage-bionetworks/synapse-types'\nimport { useEffect, useRef, useState } from 'react'\nimport { SkeletonParagraph } from '../Skeleton'\nimport { displayToast } from '../ToastMessage'\nimport AccessLevelMenu from './AccessLevelMenu'\nimport { ChatAttachment } from './utils/types'\nimport { ChatInputArea } from './components/ChatInputArea/ChatInputArea'\nimport SynapseChatMessage from './SynapseChatMessage'\nimport { SmartToyTwoTone } from '@mui/icons-material'\nimport { UserCard } from '../UserCard/UserCard'\nimport { useApplicationSessionContext } from '@/utils/AppUtils'\n\nconst DEFAULT_AVATAR = (\n <Box\n sx={{\n p: '3px',\n borderRadius: '50%',\n borderStyle: 'solid',\n borderWidth: '1px',\n borderColor: 'grey.300',\n mt: '10px',\n height: '31px',\n }}\n >\n <SmartToyTwoTone sx={{ color: 'secondary.main' }} />\n </Box>\n)\n\nexport type SynapseChatProps = {\n initialMessage?: string //optional initial message\n agentRegistrationId?: string // if provided, use this agent\n chatbotName?: string // optional name of this chatbot agent\n hideTitle?: boolean\n textboxPositionOffset?: string // when embedded in a form, the textbox (form) stuck to the bottom may need to be offset due to container padding (dialog content for example!)\n /* optional session context for the agent session */\n sessionContext?: GridAgentSessionContext\n /* The current session, if state is lifted out of this component */\n externalSession?: AgentSession\n /* A callback invoked to bootstrap the current session, if state is to be lifted out of this component */\n setExternalSession?: (s: AgentSession | undefined) => void\n /* The current chat history, if state is lifted out of this component */\n externalChatState?: ChatState\n // default access level for the agent session.\n defaultAgentAccessLevel?: AgentAccessLevel\n // Whether to show the access level menu for the agent session.\n showAccessLevelMenu?: boolean\n /**\n * Optional callback invoked once when a chat response is received from the server.\n * Use this for side effects such as navigation based on the response content.\n * Called in the mutation onSuccess handler so it runs exactly once per response.\n */\n onChatResponse?: (responseText: string) => void\n /** Optional list of prompt suggestions shown as clickable pills above the text input */\n suggestedPrompts?: string[]\n /** Optional custom avatar for the chatbot agent */\n agentAvatar?: React.ReactNode\n /**\n * Whether to allow the user to attach local files to their chat message.\n * @default false\n */\n allowAttachments?: boolean\n}\n\nexport function SynapseChat({\n initialMessage,\n agentRegistrationId,\n chatbotName = 'SynapseChat',\n agentAvatar = DEFAULT_AVATAR,\n hideTitle = false,\n textboxPositionOffset = '0px',\n sessionContext,\n externalSession,\n setExternalSession,\n externalChatState,\n showAccessLevelMenu = true,\n onChatResponse,\n suggestedPrompts,\n allowAttachments = false,\n}: SynapseChatProps) {\n const { accessToken } = useSynapseContext()\n const { userId } = useApplicationSessionContext()\n\n const userAvatar = (\n <UserCard ownerId={userId} size=\"AVATAR\" avatarSize=\"MEDIUM\" />\n )\n const [localAgentSession, setLocalAgentSession] = useState<AgentSession>()\n const agentSession = externalSession ?? localAgentSession\n const setAgentSession = setExternalSession ?? setLocalAgentSession\n\n const { mutate: createAgentSession, error: createAgentSessionError } =\n useCreateAgentSession({\n onSuccess: newAgentSession => setAgentSession(newAgentSession),\n })\n\n const { mutate: updateAgentSession } = useUpdateAgentSession({\n onSuccess: updatedAgentSession => setAgentSession(updatedAgentSession),\n onError: err =>\n displayToast(\n `Unable to update the agent session: ${err.message}`,\n 'danger',\n ),\n })\n const [agentAccessLevel, setAgentAccessLevel] = useState<AgentAccessLevel>(\n sessionContext\n ? AgentAccessLevel.WRITE_YOUR_PRIVATE_DATA\n : AgentAccessLevel.PUBLICLY_ACCESSIBLE,\n )\n\n const internalChatState = useChatState(agentSession, onChatResponse)\n const chatState = externalChatState ?? internalChatState\n const { interactions, isAwaitingResponse, sendChat } = chatState\n\n // Only interactions added after this component instance mounted should play the entry\n // animation. Callers may lift state so history survives a `Dialog` closing, but the\n // `Dialog` unmounts its children, so every reopen would otherwise re-animate the whole\n // conversation.\n const presentAtMountRef = useRef<Set<string> | undefined>(undefined)\n if (presentAtMountRef.current === undefined) {\n presentAtMountRef.current = new Set(interactions.map(i => i.id))\n }\n const presentAtMount = presentAtMountRef.current\n\n // Keep track of the text that the user is currently typing into the textfield\n const [userChatTextfieldValue, setUserChatTextfieldValue] = useState('')\n const [initialMessageProcessed, setInitialMessageProcessed] = useState(false)\n\n // A copy of the attachments sent with the last interaction, so it can still show rich\n // (name/type) chips after ChatInputArea has cleared its own attachment state. This is passed to\n // the last interaction for its entire lifetime -- sent, then processing, then completed -- as a\n // fallback filename source, since the request body (FileHandleAssociation) never carries a\n // filename and the server-resolved filename (attachmentStatuses) only arrives once the job\n // completes. It's simply overwritten on the next send.\n const [lastSentAttachments, setLastSentAttachments] = useState<\n ChatAttachment[]\n >([])\n\n // Restore chat session history, if exists.\n // TODO: currently only a single page is restored. Add support for multiple pages (and detect the user scrolling up to restore the next page of results older)\n // const {\n // data: sessionHistoryInfiniteData,\n // } = //, hasNextPage: hasMoreSessionHistory, fetchNextPage: fetchNextSessionHistoryPage, isLoading: isSessionHistoryLoading } =\n // useGetAgentChatSessionHistoryInfinite(\n // {\n // sessionId: agentSession?.sessionId,\n // },\n // {\n // enabled: !!agentSession,\n // },\n // )\n\n // const sessionHistory = useMemo(\n // () =>\n // sessionHistoryInfiniteData?.pages\n // .flatMap(page => page.page)\n // /* Note: session history is ordered from most recent to least recent in each page, so reverse the order when restoring the chat interface */\n // .reverse() ?? [],\n // [sessionHistoryInfiniteData],\n // )\n\n useEffect(() => {\n // on mount, create a new agent session!\n if (createAgentSession && !agentSession) {\n createAgentSession({\n agentAccessLevel,\n agentRegistrationId,\n sessionContext,\n })\n }\n }, [\n createAgentSession,\n agentSession,\n accessToken,\n sessionContext,\n agentAccessLevel,\n agentRegistrationId,\n ])\n\n useEffect(() => {\n // on mount, resolve the initial message chat interaction (if set)\n if (agentSession && initialMessage && !initialMessageProcessed) {\n sendChat(initialMessage)\n setInitialMessageProcessed(true)\n }\n }, [agentSession, initialMessage, initialMessageProcessed, sendChat])\n\n const handleSend = (message: string, attachments: ChatAttachment[]) => {\n sendChat(\n message,\n attachments.length\n ? attachments.map(attachment => ({\n fileHandleId: attachment.fileHandleId,\n // The backend short-circuits the FileHandleAssociation auth check for a user's own\n // uploaded file handle, so associateObjectId/associateObjectType are not totally\n // accurate here. FileHandleAssociateType has no dedicated value for \"the uploader's\n // own bare file handle\", so this stubs associateObjectId to the fileHandleId itself\n // and the FileEntity associate type, which supports the short-circuit path.\n associateObjectId: attachment.fileHandleId,\n associateObjectType: FileHandleAssociateType.FileEntity,\n }))\n : undefined,\n )\n setUserChatTextfieldValue('')\n setLastSentAttachments(attachments)\n }\n\n if (createAgentSessionError) {\n return (\n <Alert severity={'error'} sx={{ my: 2 }}>\n {createAgentSessionError.reason}\n </Alert>\n )\n }\n\n return (\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: 'space-between',\n maxWidth: '1100px',\n mx: 'auto',\n height: '100%',\n }}\n >\n {!hideTitle && (\n <Typography\n variant=\"headline1\"\n sx={{\n p: '20px',\n borderBottom: '1px solid',\n borderColor: 'grey.300',\n position: 'sticky',\n top: '0px',\n backgroundColor: 'white',\n zIndex: 1,\n }}\n >\n {chatbotName}\n </Typography>\n )}\n {showAccessLevelMenu && (\n <AccessLevelMenu\n initAccessLevel={agentAccessLevel}\n onChange={newAccessLevel => {\n setAgentAccessLevel(newAccessLevel)\n updateAgentSession({\n agentAccessLevel: newAccessLevel,\n sessionId: agentSession!.sessionId!,\n })\n }}\n />\n )}\n {!agentSession && <SkeletonParagraph numRows={10} />}\n {agentSession && (\n <Box sx={{ flexGrow: 1, overflowY: 'auto', mb: 2 }}>\n <List\n sx={{\n flex: 1,\n overflowY: 'auto',\n pt: '20px',\n display: 'flex',\n flexDirection: 'column',\n }}\n >\n {/* {sessionHistory &&\n sessionHistory.map((interaction, index) => {\n return (\n <SynapseChatInteraction\n key={index}\n userMessage={interaction.usersRequestText}\n chatResponseText={interaction.agentResponseText}\n />\n )\n })} */}\n {interactions.map((interaction, index) => {\n const isLast = index === interactions.length - 1\n return (\n <SynapseChatMessage\n agentAvatar={agentAvatar}\n userAvatar={userAvatar}\n key={interaction.id}\n userMessage={interaction.userMessage}\n chatJobId={interaction.jobId}\n onSendChat={sendChat}\n scrollIntoView={isLast}\n animateEntry={!presentAtMount.has(interaction.id)}\n isAwaitingResponse={isAwaitingResponse}\n pendingAttachments={\n // Only one interaction can be in flight at a time (the composer is disabled\n // while isAwaitingResponse), so lastSentAttachments always corresponds to\n // whichever interaction is last.\n isLast ? lastSentAttachments : undefined\n }\n />\n )\n })}\n </List>\n </Box>\n )}\n <Box\n sx={{\n position: 'sticky',\n bottom: textboxPositionOffset,\n backgroundColor: 'white',\n }}\n >\n {suggestedPrompts &&\n suggestedPrompts.length > 0 &&\n interactions.length === 0 && (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ pt: '8px', flexWrap: 'wrap', gap: 1 }}\n >\n {suggestedPrompts.map(prompt => (\n <Chip\n key={prompt}\n label={prompt}\n variant=\"outlined\"\n onClick={() => setUserChatTextfieldValue(prompt)}\n disabled={!agentSession}\n sx={{ cursor: 'pointer' }}\n />\n ))}\n </Stack>\n )}\n <Box sx={{ mt: '5px' }}>\n <ChatInputArea\n value={userChatTextfieldValue}\n onValueChange={setUserChatTextfieldValue}\n onSend={handleSend}\n placeholder={`Message ${chatbotName}`}\n disabled={!agentSession || isAwaitingResponse}\n allowAttachments={allowAttachments}\n />\n </Box>\n </Box>\n <Typography variant=\"smallText1\" sx={{ pt: '8px', textAlign: 'center' }}>\n {chatbotName} can make mistakes.\n </Typography>\n </Box>\n )\n}\n\nexport default SynapseChat\n"],"mappings":"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"}
1
+ {"version":3,"file":"SynapseChat.js","names":[],"sources":["../../../src/components/SynapseChat/SynapseChat.tsx"],"sourcesContent":["import { ChatState, useChatState } from '@/components/SynapseChat/useChatState'\nimport {\n useCreateAgentSession,\n useUpdateAgentSession,\n} from '@/synapse-queries/chat/useChat'\nimport { useSynapseContext } from '@/utils'\nimport {\n Alert,\n Box,\n Chip,\n List,\n ListItem,\n Stack,\n Typography,\n} from '@mui/material'\nimport {\n AgentSession,\n FileHandleAssociateType,\n GridAgentSessionContext,\n} from '@sage-bionetworks/synapse-client'\nimport { AgentAccessLevel } from '@sage-bionetworks/synapse-types'\nimport { useEffect, useRef, useState } from 'react'\nimport { SkeletonParagraph } from '../Skeleton'\nimport { displayToast } from '../ToastMessage'\nimport AccessLevelMenu from './AccessLevelMenu'\nimport { ChatAttachment } from './utils/types'\nimport { ChatInputArea } from './components/ChatInputArea/ChatInputArea'\nimport SynapseChatMessage from './SynapseChatMessage'\nimport { SmartToyTwoTone } from '@mui/icons-material'\nimport { UserCard } from '../UserCard/UserCard'\nimport { useApplicationSessionContext } from '@/utils/AppUtils'\nimport { ReactComponent as CurieAvatarHead } from '@/assets/illustrations/curie_avatar_head.svg'\n\nconst CURIE_GREETING = 'Hi! How can I help you today?'\n\nconst CURIE_ACCESS_LEVEL_SUFFIX: Record<AgentAccessLevel, string> = {\n [AgentAccessLevel.PUBLICLY_ACCESSIBLE]: ' and can only read public data.',\n [AgentAccessLevel.READ_YOUR_PRIVATE_DATA]:\n ' and can read your public and private data in Synapse on your behalf.',\n [AgentAccessLevel.WRITE_YOUR_PRIVATE_DATA]:\n ' and can read and write your public and private data in Synapse on your behalf.',\n}\n\nconst DEFAULT_AVATAR = (\n <Box\n sx={{\n p: '3px',\n borderRadius: '50%',\n borderStyle: 'solid',\n borderWidth: '1px',\n borderColor: 'grey.300',\n mt: '10px',\n height: '31px',\n }}\n >\n <SmartToyTwoTone sx={{ color: 'secondary.main' }} />\n </Box>\n)\n\nconst CURIE_AVATAR = (\n <Box\n sx={{\n mt: '10px',\n height: '30px',\n width: '30px',\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <CurieAvatarHead />\n </Box>\n)\n\nexport type SynapseChatProps = {\n initialMessage?: string //optional initial message\n agentRegistrationId?: string // if provided, use this agent\n chatbotName?: string // optional name of this chatbot agent\n variant?: 'default' | 'curie'\n hideTitle?: boolean\n textboxPositionOffset?: string // when embedded in a form, the textbox (form) stuck to the bottom may need to be offset due to container padding (dialog content for example!)\n /* optional session context for the agent session */\n sessionContext?: GridAgentSessionContext\n /* The current session, if state is lifted out of this component */\n externalSession?: AgentSession\n /* A callback invoked to bootstrap the current session, if state is to be lifted out of this component */\n setExternalSession?: (s: AgentSession | undefined) => void\n /* The current chat history, if state is lifted out of this component */\n externalChatState?: ChatState\n // default access level for the agent session.\n defaultAgentAccessLevel?: AgentAccessLevel\n // Whether to show the access level menu for the agent session.\n showAccessLevelMenu?: boolean\n /**\n * Optional callback invoked once when a chat response is received from the server.\n * Use this for side effects such as navigation based on the response content.\n * Called in the mutation onSuccess handler so it runs exactly once per response.\n */\n onChatResponse?: (responseText: string) => void\n /** Optional list of prompt suggestions shown as clickable pills above the text input */\n suggestedPrompts?: string[]\n /** Optional custom avatar for the chatbot agent */\n agentAvatar?: React.ReactNode\n /**\n * Whether to allow the user to attach local files to their chat message.\n * @default false\n */\n allowAttachments?: boolean\n}\n\nexport function SynapseChat({\n initialMessage,\n agentRegistrationId,\n chatbotName = 'SynapseChat',\n variant = 'default',\n agentAvatar,\n hideTitle = false,\n textboxPositionOffset = '0px',\n sessionContext,\n externalSession,\n setExternalSession,\n externalChatState,\n showAccessLevelMenu = true,\n onChatResponse,\n suggestedPrompts,\n allowAttachments = false,\n}: SynapseChatProps) {\n const { accessToken } = useSynapseContext()\n const { userId } = useApplicationSessionContext()\n\n const baseDisclaimerText = chatbotName + ' can make mistakes'\n\n const resolvedAgentAvatar =\n agentAvatar ?? (variant === 'curie' ? CURIE_AVATAR : DEFAULT_AVATAR)\n\n const userAvatar = (\n <UserCard ownerId={userId} size=\"AVATAR\" avatarSize=\"MEDIUM\" />\n )\n const [localAgentSession, setLocalAgentSession] = useState<AgentSession>()\n const agentSession = externalSession ?? localAgentSession\n const setAgentSession = setExternalSession ?? setLocalAgentSession\n\n const { mutate: createAgentSession, error: createAgentSessionError } =\n useCreateAgentSession({\n onSuccess: newAgentSession => setAgentSession(newAgentSession),\n })\n\n const { mutate: updateAgentSession } = useUpdateAgentSession({\n onSuccess: updatedAgentSession => setAgentSession(updatedAgentSession),\n onError: err =>\n displayToast(\n `Unable to update the agent session: ${err.message}`,\n 'danger',\n ),\n })\n const [agentAccessLevel, setAgentAccessLevel] = useState<AgentAccessLevel>(\n sessionContext\n ? AgentAccessLevel.WRITE_YOUR_PRIVATE_DATA\n : AgentAccessLevel.PUBLICLY_ACCESSIBLE,\n )\n\n const internalChatState = useChatState(agentSession, onChatResponse)\n const chatState = externalChatState ?? internalChatState\n const { interactions, isAwaitingResponse, sendChat } = chatState\n\n const showGreeting = variant === 'curie' && interactions.length === 0\n\n const curieAccessLevelSuffix =\n variant === 'curie' ? CURIE_ACCESS_LEVEL_SUFFIX[agentAccessLevel] : ''\n const disclaimerText = baseDisclaimerText + curieAccessLevelSuffix\n\n // Only interactions added after this component instance mounted should play the entry\n // animation. Callers may lift state so history survives a `Dialog` closing, but the\n // `Dialog` unmounts its children, so every reopen would otherwise re-animate the whole\n // conversation.\n const presentAtMountRef = useRef<Set<string> | undefined>(undefined)\n if (presentAtMountRef.current === undefined) {\n presentAtMountRef.current = new Set(interactions.map(i => i.id))\n }\n const presentAtMount = presentAtMountRef.current\n\n // Keep track of the text that the user is currently typing into the textfield\n const [userChatTextfieldValue, setUserChatTextfieldValue] = useState('')\n const [initialMessageProcessed, setInitialMessageProcessed] = useState(false)\n\n // A copy of the attachments sent with the last interaction, so it can still show rich\n // (name/type) chips after ChatInputArea has cleared its own attachment state. This is passed to\n // the last interaction for its entire lifetime -- sent, then processing, then completed -- as a\n // fallback filename source, since the request body (FileHandleAssociation) never carries a\n // filename and the server-resolved filename (attachmentStatuses) only arrives once the job\n // completes. It's simply overwritten on the next send.\n const [lastSentAttachments, setLastSentAttachments] = useState<\n ChatAttachment[]\n >([])\n\n // Restore chat session history, if exists.\n // TODO: currently only a single page is restored. Add support for multiple pages (and detect the user scrolling up to restore the next page of results older)\n // const {\n // data: sessionHistoryInfiniteData,\n // } = //, hasNextPage: hasMoreSessionHistory, fetchNextPage: fetchNextSessionHistoryPage, isLoading: isSessionHistoryLoading } =\n // useGetAgentChatSessionHistoryInfinite(\n // {\n // sessionId: agentSession?.sessionId,\n // },\n // {\n // enabled: !!agentSession,\n // },\n // )\n\n // const sessionHistory = useMemo(\n // () =>\n // sessionHistoryInfiniteData?.pages\n // .flatMap(page => page.page)\n // /* Note: session history is ordered from most recent to least recent in each page, so reverse the order when restoring the chat interface */\n // .reverse() ?? [],\n // [sessionHistoryInfiniteData],\n // )\n\n useEffect(() => {\n // on mount, create a new agent session!\n if (createAgentSession && !agentSession) {\n createAgentSession({\n agentAccessLevel,\n agentRegistrationId,\n sessionContext,\n })\n }\n }, [\n createAgentSession,\n agentSession,\n accessToken,\n sessionContext,\n agentAccessLevel,\n agentRegistrationId,\n ])\n\n useEffect(() => {\n // on mount, resolve the initial message chat interaction (if set)\n if (agentSession && initialMessage && !initialMessageProcessed) {\n sendChat(initialMessage)\n setInitialMessageProcessed(true)\n }\n }, [agentSession, initialMessage, initialMessageProcessed, sendChat])\n\n const handleSend = (message: string, attachments: ChatAttachment[]) => {\n sendChat(\n message,\n attachments.length\n ? attachments.map(attachment => ({\n fileHandleId: attachment.fileHandleId,\n // The backend short-circuits the FileHandleAssociation auth check for a user's own\n // uploaded file handle, so associateObjectId/associateObjectType are not totally\n // accurate here. FileHandleAssociateType has no dedicated value for \"the uploader's\n // own bare file handle\", so this stubs associateObjectId to the fileHandleId itself\n // and the FileEntity associate type, which supports the short-circuit path.\n associateObjectId: attachment.fileHandleId,\n associateObjectType: FileHandleAssociateType.FileEntity,\n }))\n : undefined,\n )\n setUserChatTextfieldValue('')\n setLastSentAttachments(attachments)\n }\n\n if (createAgentSessionError) {\n return (\n <Alert severity={'error'} sx={{ my: 2 }}>\n {createAgentSessionError.reason}\n </Alert>\n )\n }\n\n return (\n <Box\n sx={{\n display: 'flex',\n flexDirection: 'column',\n justifyContent: variant === 'curie' ? 'flex-start' : 'space-between',\n gap: variant === 'curie' ? '20px' : '0',\n maxWidth: '1100px',\n mx: 'auto',\n height: '100%',\n }}\n >\n {!hideTitle && (\n <Typography\n variant=\"headline1\"\n sx={{\n p: '20px',\n borderBottom: '1px solid',\n borderColor: 'grey.300',\n position: 'sticky',\n top: '0px',\n backgroundColor: 'white',\n zIndex: 1,\n }}\n >\n {chatbotName}\n </Typography>\n )}\n {showAccessLevelMenu && variant !== 'curie' && (\n <AccessLevelMenu\n initAccessLevel={agentAccessLevel}\n onChange={newAccessLevel => {\n setAgentAccessLevel(newAccessLevel)\n updateAgentSession({\n agentAccessLevel: newAccessLevel,\n sessionId: agentSession!.sessionId!,\n })\n }}\n />\n )}\n {!agentSession && <SkeletonParagraph numRows={10} />}\n {agentSession && (\n <Box\n sx={{\n flexGrow: 1,\n overflowY: 'auto',\n mb: variant === 'curie' ? 0 : 2,\n }}\n >\n <List\n sx={{\n flex: 1,\n overflowY: 'auto',\n pt: '20px',\n ...(variant === 'curie' && { pt: 0, pb: 0, gap: '20px' }),\n display: 'flex',\n flexDirection: 'column',\n ...(showGreeting && {\n minHeight: '100%',\n justifyContent: 'flex-end',\n }),\n }}\n >\n {/* {sessionHistory &&\n sessionHistory.map((interaction, index) => {\n return (\n <SynapseChatInteraction\n key={index}\n userMessage={interaction.usersRequestText}\n chatResponseText={interaction.agentResponseText}\n />\n )\n })} */}\n {showGreeting && (\n <ListItem\n sx={{\n display: 'grid',\n gridTemplateColumns: '50px auto',\n columnGap: '16px',\n justifyItems: 'start',\n alignItems: 'start',\n p: 0,\n }}\n >\n {resolvedAgentAvatar}\n <Typography sx={{ justifySelf: 'start', mt: '14px' }}>\n {CURIE_GREETING}\n </Typography>\n </ListItem>\n )}\n {interactions.map((interaction, index) => {\n const isLast = index === interactions.length - 1\n return (\n <SynapseChatMessage\n agentAvatar={resolvedAgentAvatar}\n userAvatar={userAvatar}\n key={interaction.id}\n userMessage={interaction.userMessage}\n chatJobId={interaction.jobId}\n showLoadingIcon={variant !== 'curie'}\n onSendChat={sendChat}\n scrollIntoView={isLast}\n animateEntry={!presentAtMount.has(interaction.id)}\n isAwaitingResponse={isAwaitingResponse}\n pendingAttachments={\n // Only one interaction can be in flight at a time (the composer is disabled\n // while isAwaitingResponse), so lastSentAttachments always corresponds to\n // whichever interaction is last.\n isLast ? lastSentAttachments : undefined\n }\n />\n )\n })}\n </List>\n </Box>\n )}\n <Box\n sx={{\n position: 'sticky',\n bottom: variant === 'curie' ? '0px' : textboxPositionOffset,\n backgroundColor: 'white',\n }}\n >\n {suggestedPrompts &&\n suggestedPrompts.length > 0 &&\n interactions.length === 0 && (\n <Stack\n direction=\"row\"\n spacing={1}\n sx={{ pt: '8px', flexWrap: 'wrap', gap: 1 }}\n >\n {suggestedPrompts.map(prompt => (\n <Chip\n key={prompt}\n label={prompt}\n variant=\"outlined\"\n onClick={() => setUserChatTextfieldValue(prompt)}\n disabled={!agentSession}\n sx={{ cursor: 'pointer' }}\n />\n ))}\n </Stack>\n )}\n <Box sx={{ mt: '5px' }}>\n <ChatInputArea\n value={userChatTextfieldValue}\n onValueChange={setUserChatTextfieldValue}\n onSend={handleSend}\n placeholder={\n variant === 'curie' ? 'Ask anything' : `Message ${chatbotName}`\n }\n disabled={!agentSession || isAwaitingResponse}\n allowAttachments={allowAttachments}\n />\n </Box>\n </Box>\n <Typography variant=\"smallText1\" sx={{ pt: '8px', textAlign: 'center' }}>\n {disclaimerText}\n </Typography>\n </Box>\n )\n}\n\nexport default SynapseChat\n"],"mappings":"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"}
@@ -28,7 +28,9 @@ export type SynapseChatInteractionProps = {
28
28
  attachments?: SynapseChatInteractionAttachment[];
29
29
  /** Server-reported outcome of staging each attachment, if available (see AgentChatResponseWithAttachmentStatuses). */
30
30
  attachmentStatuses?: AgentChatAttachmentStatus[];
31
+ /** Whether to show the loading spinner icon while the response is in flight. */
32
+ showLoadingIcon?: boolean;
31
33
  };
32
- export declare function SynapseChatInteraction({ userMessage, chatResponseText, chatErrorReason, chatResponseTrace, scrollIntoView, onSendChat, agentAvatar, userAvatar, animateEntry, isAwaitingResponse, attachments, attachmentStatuses, }: SynapseChatInteractionProps): import("react/jsx-runtime").JSX.Element;
34
+ export declare function SynapseChatInteraction({ userMessage, chatResponseText, chatErrorReason, chatResponseTrace, scrollIntoView, onSendChat, agentAvatar, userAvatar, animateEntry, isAwaitingResponse, attachments, attachmentStatuses, showLoadingIcon, }: SynapseChatInteractionProps): import("react/jsx-runtime").JSX.Element;
33
35
  export default SynapseChatInteraction;
34
36
  //# sourceMappingURL=SynapseChatInteraction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseChatInteraction.d.ts","sourceRoot":"","sources":["../../../src/components/SynapseChat/SynapseChatInteraction.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAQ5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAA;AAE5E;;;;;GAKG;AACH,MAAM,MAAM,gCAAgC,GAAG;IAC7C,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iBAAiB,CAAC,EAAE,UAAU,EAAE,CAAA;IAChC,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,UAAU,EAAE,KAAK,CAAC,SAAS,CAAA;IAC3B,sFAAsF;IACtF,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,6GAA6G;IAC7G,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,2DAA2D;IAC3D,WAAW,CAAC,EAAE,gCAAgC,EAAE,CAAA;IAChD,sHAAsH;IACtH,kBAAkB,CAAC,EAAE,yBAAyB,EAAE,CAAA;CACjD,CAAA;AAqBD,wBAAgB,sBAAsB,CAAC,EACrC,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,cAAsB,EACtB,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAoB,EACpB,kBAA0B,EAC1B,WAAW,EACX,kBAAkB,GACnB,EAAE,2BAA2B,2CAmP7B;AAED,eAAe,sBAAsB,CAAA"}
1
+ {"version":3,"file":"SynapseChatInteraction.d.ts","sourceRoot":"","sources":["../../../src/components/SynapseChat/SynapseChatInteraction.tsx"],"names":[],"mappings":"AAiBA,OAAO,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAQ5D,OAAO,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAA;AAE5E;;;;;GAKG;AACH,MAAM,MAAM,gCAAgC,GAAG;IAC7C,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,MAAM,MAAM,2BAA2B,GAAG;IACxC,WAAW,EAAE,MAAM,CAAA;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,iBAAiB,CAAC,EAAE,UAAU,EAAE,CAAA;IAChC,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,eAAe,CAAC,EAAE,MAAM,CAAA;IACxB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IACtC,WAAW,EAAE,KAAK,CAAC,SAAS,CAAA;IAC5B,UAAU,EAAE,KAAK,CAAC,SAAS,CAAA;IAC3B,sFAAsF;IACtF,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB,6GAA6G;IAC7G,kBAAkB,CAAC,EAAE,OAAO,CAAA;IAC5B,2DAA2D;IAC3D,WAAW,CAAC,EAAE,gCAAgC,EAAE,CAAA;IAChD,sHAAsH;IACtH,kBAAkB,CAAC,EAAE,yBAAyB,EAAE,CAAA;IAChD,gFAAgF;IAChF,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B,CAAA;AAqBD,wBAAgB,sBAAsB,CAAC,EACrC,WAAW,EACX,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,cAAsB,EACtB,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAoB,EACpB,kBAA0B,EAC1B,WAAW,EACX,kBAAkB,EAClB,eAAsB,GACvB,EAAE,2BAA2B,2CAqP7B;AAED,eAAe,sBAAsB,CAAA"}
@@ -11,17 +11,17 @@ import { KeyboardArrowDown as S, KeyboardArrowRight as C } from "@mui/icons-mate
11
11
  function w(e) {
12
12
  return "reasoningText" in e ? e.reasoningText : "";
13
13
  }
14
- function T({ userMessage: T, chatResponseText: E, chatErrorReason: D, chatResponseTrace: O, scrollIntoView: k = !1, onSendChat: A, agentAvatar: j, userAvatar: M, animateEntry: N = !1, isAwaitingResponse: P = !1, attachments: F, attachmentStatuses: I }) {
15
- let L = v(), R = s(null), z = s(null), [B, V] = c(!1), H = !E && !D;
14
+ function T({ userMessage: T, chatResponseText: E, chatErrorReason: D, chatResponseTrace: O, scrollIntoView: k = !1, onSendChat: A, agentAvatar: j, userAvatar: M, animateEntry: N = !1, isAwaitingResponse: P = !1, attachments: F, attachmentStatuses: I, showLoadingIcon: L = !0 }) {
15
+ let R = v(), z = s(null), B = s(null), [V, H] = c(!1), U = !E && !D;
16
16
  a(() => {
17
- k && R.current?.scrollIntoView({ behavior: "smooth" });
17
+ k && z.current?.scrollIntoView({ behavior: "smooth" });
18
18
  }, [k]), a(() => {
19
- k && !H && z.current?.scrollIntoView({
19
+ k && !U && B.current?.scrollIntoView({
20
20
  behavior: "smooth",
21
21
  block: "end"
22
22
  });
23
- }, [k, H]);
24
- let U = o(() => (O ?? []).flatMap((e) => r(e))?.filter((e) => !!e), [O]), W = U.length > 0, G = o(() => U.filter((e) => "reasoningText" in e).at(-1), [U]), K = G == null ? "Thinking..." : G.reasoningText, q = H ? K : `${B ? "Hide" : "Show"} Trace`, { textContent: J, guidePrompts: Y } = o(() => {
23
+ }, [k, U]);
24
+ let W = o(() => (O ?? []).flatMap((e) => r(e))?.filter((e) => !!e), [O]), G = W.length > 0, K = o(() => W.filter((e) => "reasoningText" in e).at(-1), [W]), q = K == null ? "Thinking..." : K.reasoningText, J = U ? q : `${V ? "Hide" : "Show"} Trace`, { textContent: Y, guidePrompts: X } = o(() => {
25
25
  try {
26
26
  let e = new DOMParser().parseFromString(E ?? "", "text/html");
27
27
  e.querySelectorAll("tool_name, actions").forEach((e) => e.remove());
@@ -38,7 +38,7 @@ function T({ userMessage: T, chatResponseText: E, chatErrorReason: D, chatRespon
38
38
  guidePrompts: []
39
39
  };
40
40
  }
41
- }, [E]), X = o(() => (F ?? []).map((e) => {
41
+ }, [E]), Z = o(() => (F ?? []).map((e) => {
42
42
  let t = I?.find((t) => t.fileHandleId === e.fileHandleId);
43
43
  return {
44
44
  fileHandleId: e.fileHandleId,
@@ -50,7 +50,7 @@ function T({ userMessage: T, chatResponseText: E, chatErrorReason: D, chatRespon
50
50
  }), [F, I]);
51
51
  return /* @__PURE__ */ x(y, { children: [
52
52
  /* @__PURE__ */ x(m, {
53
- ref: R,
53
+ ref: z,
54
54
  sx: {
55
55
  alignSelf: "flex-end",
56
56
  maxWidth: "82%",
@@ -76,19 +76,19 @@ function T({ userMessage: T, chatResponseText: E, chatErrorReason: D, chatRespon
76
76
  children: M
77
77
  })]
78
78
  }),
79
- X.length > 0 && /* @__PURE__ */ b(m, {
79
+ Z.length > 0 && /* @__PURE__ */ b(m, {
80
80
  sx: {
81
81
  display: "block",
82
82
  p: "0px",
83
83
  mb: "5px"
84
84
  },
85
85
  children: /* @__PURE__ */ b(n, {
86
- items: X,
86
+ items: Z,
87
87
  wrap: !0
88
88
  })
89
89
  }),
90
90
  /* @__PURE__ */ x(m, {
91
- ref: z,
91
+ ref: B,
92
92
  sx: {
93
93
  display: "grid",
94
94
  gridTemplateColumns: "50px auto",
@@ -108,7 +108,7 @@ function T({ userMessage: T, chatResponseText: E, chatErrorReason: D, chatRespon
108
108
  component: "aside",
109
109
  sx: { ".markdown": {
110
110
  fontStyle: "italic",
111
- borderLeft: `4px solid ${L.palette.grey[300]}`,
111
+ borderLeft: `4px solid ${R.palette.grey[300]}`,
112
112
  marginLeft: "4px",
113
113
  paddingLeft: "8px",
114
114
  mt: 1,
@@ -117,10 +117,10 @@ function T({ userMessage: T, chatResponseText: E, chatErrorReason: D, chatRespon
117
117
  children: [/* @__PURE__ */ b(d, {
118
118
  variant: "outlined",
119
119
  size: "small",
120
- startIcon: H ? /* @__PURE__ */ b(e, { size: 14 }) : b(B ? S : C, { sx: { width: "14px" } }),
121
- disabled: !W,
120
+ startIcon: U ? L ? /* @__PURE__ */ b(e, { size: 14 }) : void 0 : b(V ? S : C, { sx: { width: "14px" } }),
121
+ disabled: !G,
122
122
  onClick: () => {
123
- V((e) => !e);
123
+ H((e) => !e);
124
124
  },
125
125
  sx: {
126
126
  height: "20px",
@@ -141,20 +141,20 @@ function T({ userMessage: T, chatResponseText: E, chatErrorReason: D, chatRespon
141
141
  overflow: "hidden",
142
142
  textOverflow: "ellipsis"
143
143
  },
144
- children: q
144
+ children: J
145
145
  })
146
146
  }), O && /* @__PURE__ */ b(p, {
147
- in: B,
148
- children: /* @__PURE__ */ b(t, { markdown: U.map(w).filter((e) => e.trim().length > 0).join("<br/><br/>") })
147
+ in: V,
148
+ children: /* @__PURE__ */ b(t, { markdown: W.map(w).filter((e) => e.trim().length > 0).join("<br/><br/>") })
149
149
  })]
150
- }), (J || A && Y.length > 0) && /* @__PURE__ */ x(u, {
150
+ }), (Y || A && X.length > 0) && /* @__PURE__ */ x(u, {
151
151
  className: N ? i.entryAnimation : void 0,
152
- children: [J && /* @__PURE__ */ b(t, { markdown: J }), A && Y.length > 0 && /* @__PURE__ */ b(g, {
152
+ children: [Y && /* @__PURE__ */ b(t, { markdown: Y }), A && X.length > 0 && /* @__PURE__ */ b(g, {
153
153
  direction: "row",
154
154
  flexWrap: "wrap",
155
155
  gap: 1,
156
156
  mt: 1,
157
- children: Y.map((e) => /* @__PURE__ */ b(_, {
157
+ children: X.map((e) => /* @__PURE__ */ b(_, {
158
158
  title: e,
159
159
  children: /* @__PURE__ */ b(f, {
160
160
  label: e,
@@ -1 +1 @@
1
- {"version":3,"file":"SynapseChatInteraction.js","names":[],"sources":["../../../src/components/SynapseChat/SynapseChatInteraction.tsx"],"sourcesContent":["import { SynapseSpinner } from '@/components/LoadingScreen/LoadingScreen'\nimport extractMessageFromTraceEvent, {\n TraceMessage,\n} from '@/components/SynapseChat/extractMessageFromTraceEvent'\nimport { KeyboardArrowDown, KeyboardArrowRight } from '@mui/icons-material'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Collapse,\n ListItem,\n ListItemText,\n Stack,\n Tooltip,\n useTheme,\n} from '@mui/material'\nimport { TraceEvent } from '@sage-bionetworks/synapse-types'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport MarkdownSynapse from '../Markdown/MarkdownSynapse'\nimport styles from './SynapseChatInteraction.module.scss'\nimport {\n AttachmentStripItem,\n ChatAttachmentStrip,\n} from './components/ChatAttachmentStrip/ChatAttachmentStrip'\nimport { AgentChatAttachmentStatus } from '@sage-bionetworks/synapse-client'\n\n/**\n * A chat attachment to display on a sent/pending turn. Only `fileHandleId` is guaranteed --\n * `fileName`/`contentType` are omitted for a turn restored from history, where only the\n * fileHandleId that was sent to the server is known, with no in-session upload record to fall\n * back on (see SynapseChatMessage).\n */\nexport type SynapseChatInteractionAttachment = {\n fileHandleId: string\n fileName?: string\n contentType?: string\n}\n\nexport type SynapseChatInteractionProps = {\n userMessage: string\n chatResponseText?: string\n chatResponseTrace?: TraceEvent[]\n scrollIntoView?: boolean\n chatErrorReason?: string\n onSendChat?: (message: string) => void\n agentAvatar: React.ReactNode\n userAvatar: React.ReactNode\n /** Whether to play the fade/rise-in entry animation for this interaction on mount. */\n animateEntry?: boolean\n /** Whether a turn is currently in flight; gates the guide-prompt chips so they can't bypass the composer. */\n isAwaitingResponse?: boolean\n /** Attachments the user sent with this message, if any. */\n attachments?: SynapseChatInteractionAttachment[]\n /** Server-reported outcome of staging each attachment, if available (see AgentChatResponseWithAttachmentStatuses). */\n attachmentStatuses?: AgentChatAttachmentStatus[]\n}\n\n// Show tool calls in the trace. Useful for development. We may want to show them to users in the future.\nconst SHOW_TOOL_USE = false\n\nfunction getMarkdownForTraceMessage(traceMessage: TraceMessage): string {\n if ('reasoningText' in traceMessage) {\n return traceMessage.reasoningText\n }\n if (SHOW_TOOL_USE && 'toolName' in traceMessage) {\n return `**Tool Used:** \\`${\n traceMessage.toolName\n }\\`\\n\\n**Tool Input:**\\n\\`\\`\\`json\\n${JSON.stringify(\n traceMessage.toolInput,\n null,\n 2,\n )}\\n\\`\\`\\`\\n`\n }\n return ''\n}\n\nexport function SynapseChatInteraction({\n userMessage,\n chatResponseText,\n chatErrorReason,\n chatResponseTrace,\n scrollIntoView = false,\n onSendChat,\n agentAvatar,\n userAvatar,\n animateEntry = false,\n isAwaitingResponse = false,\n attachments,\n attachmentStatuses,\n}: SynapseChatInteractionProps) {\n const theme = useTheme()\n const userMessageRef = useRef<HTMLLIElement | null>(null)\n const responseRef = useRef<HTMLLIElement | null>(null)\n const [showTrace, setShowTrace] = useState(false)\n\n const isLoading = !chatResponseText && !chatErrorReason\n\n useEffect(() => {\n // on mount, scroll into view if instructed. This brings the \"Thinking...\" row to the top so\n // the answer's opening lines are usually visible as soon as it starts streaming in.\n if (scrollIntoView) {\n userMessageRef.current?.scrollIntoView({ behavior: 'smooth' })\n }\n }, [scrollIntoView])\n\n useEffect(() => {\n // once the response (or an error) arrives, scroll all the way to the bottom so the full\n // answer - which may be taller than the viewport - is brought into view.\n if (scrollIntoView && !isLoading) {\n responseRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })\n }\n }, [scrollIntoView, isLoading])\n\n const traceMessages = useMemo(\n () =>\n (chatResponseTrace ?? [])\n .flatMap(traceEvent => extractMessageFromTraceEvent(traceEvent))\n ?.filter(trace => !!trace),\n [chatResponseTrace],\n )\n\n const hasTraceInfo = traceMessages.length > 0\n\n const lastReasoningMessage = useMemo(\n () => traceMessages.filter(trace => 'reasoningText' in trace).at(-1),\n [traceMessages],\n )\n\n const traceButtonLoadingText =\n lastReasoningMessage == null\n ? 'Thinking...'\n : lastReasoningMessage.reasoningText\n\n const traceButtonText = isLoading\n ? traceButtonLoadingText\n : `${showTrace ? 'Hide' : 'Show'} Trace`\n\n const { textContent, guidePrompts } = useMemo(() => {\n try {\n const parser = new DOMParser()\n const doc = parser.parseFromString(chatResponseText ?? '', 'text/html')\n\n // Remove tool_name and actions elements (AI XML tags not for display)\n doc\n .querySelectorAll('tool_name, actions')\n .forEach(element => element.remove())\n\n // Extract guideprompts before removing them\n const extractedGuidePrompts = Array.from(\n doc.querySelectorAll('guideprompt'),\n )\n .map(el => el.textContent?.trim())\n .filter((t): t is string => !!t)\n\n // Remove guideprompt elements\n doc.querySelectorAll('guideprompt').forEach(element => element.remove())\n\n // Extract text from <chat> element if present, otherwise use full body text\n const chatElement = doc.querySelector('chat')\n return {\n textContent: chatElement\n ? (chatElement.textContent ?? '')\n : (doc.body.textContent ?? ''),\n guidePrompts: extractedGuidePrompts,\n }\n } catch (e) {\n console.error(e)\n return { textContent: chatResponseText ?? '', guidePrompts: [] }\n }\n }, [chatResponseText])\n\n const attachmentItems: AttachmentStripItem[] = useMemo(\n () =>\n (attachments ?? []).map(attachment => {\n const status = attachmentStatuses?.find(\n s => s.fileHandleId === attachment.fileHandleId,\n )\n return {\n fileHandleId: attachment.fileHandleId,\n label: attachment.fileName ?? attachment.fileHandleId,\n contentType: attachment.contentType,\n status: status?.status === 'FAILED' ? 'failed' : 'default',\n errorMessage: status?.failureMessage,\n }\n }),\n [attachments, attachmentStatuses],\n )\n\n return (\n <>\n <ListItem\n ref={userMessageRef}\n sx={{\n alignSelf: 'flex-end',\n maxWidth: '82%',\n mb: '5px',\n wordWrap: 'break-word',\n width: 'auto',\n display: 'grid',\n gridTemplateColumns: 'auto auto',\n columnGap: '16px',\n alignItems: 'start',\n justifyItems: 'end',\n }}\n >\n <Box\n className={animateEntry ? styles.entryAnimation : undefined}\n sx={{\n p: '4px 14px',\n backgroundColor: '#F3F6F7',\n borderRadius: '8px',\n }}\n >\n <ListItemText primary={userMessage} />\n </Box>\n <Box sx={{ marginTop: '4px' }}>{userAvatar}</Box>\n </ListItem>\n {attachmentItems.length > 0 && (\n <ListItem sx={{ display: 'block', p: '0px', mb: '5px' }}>\n <ChatAttachmentStrip items={attachmentItems} wrap />\n </ListItem>\n )}\n <ListItem\n ref={responseRef}\n sx={{\n display: 'grid',\n gridTemplateColumns: '50px auto',\n columnGap: '16px',\n justifyItems: 'center',\n alignItems: 'start',\n p: '0px',\n padding: '10px',\n }}\n >\n {agentAvatar}\n <Box\n sx={{\n borderRadius: '10px',\n maxWidth: '100%',\n overflow: 'auto',\n }}\n >\n <Box\n component={'aside'}\n sx={{\n '.markdown': {\n fontStyle: 'italic',\n borderLeft: `4px solid ${theme.palette.grey[300]}`,\n marginLeft: '4px',\n paddingLeft: '8px',\n mt: 1,\n mb: 2,\n },\n }}\n >\n <Button\n variant={'outlined'}\n size={'small'}\n startIcon={\n isLoading ? (\n <SynapseSpinner size={14} />\n ) : showTrace ? (\n <KeyboardArrowDown sx={{ width: '14px' }} />\n ) : (\n <KeyboardArrowRight sx={{ width: '14px' }} />\n )\n }\n disabled={!hasTraceInfo}\n onClick={() => {\n setShowTrace(v => !v)\n }}\n sx={{\n height: '20px',\n fontSize: '12px',\n fontWeight: 600,\n mb: 1,\n border: 'none !important',\n color: 'grey.700',\n justifyContent: 'flex-start',\n whiteSpace: 'nowrap',\n maxWidth: '325px',\n textTransform: 'none',\n pl: 0,\n '.MuiButton-startIcon': { mr: 0.5 },\n }}\n >\n <Box sx={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>\n {traceButtonText}\n </Box>\n </Button>\n {chatResponseTrace && (\n <Collapse in={showTrace}>\n <MarkdownSynapse\n markdown={traceMessages\n .map(getMarkdownForTraceMessage)\n .filter(md => md.trim().length > 0)\n .join('<br/><br/>')}\n />\n </Collapse>\n )}\n </Box>\n {(textContent || (onSendChat && guidePrompts.length > 0)) && (\n <Box className={animateEntry ? styles.entryAnimation : undefined}>\n {textContent && <MarkdownSynapse markdown={textContent} />}\n {onSendChat && guidePrompts.length > 0 && (\n <Stack direction=\"row\" flexWrap=\"wrap\" gap={1} mt={1}>\n {guidePrompts.map(prompt => (\n <Tooltip key={prompt} title={prompt}>\n <Chip\n label={prompt}\n variant=\"outlined\"\n color=\"primary\"\n clickable\n disabled={isAwaitingResponse}\n onClick={() => onSendChat(prompt)}\n sx={{ maxWidth: 200 }}\n />\n </Tooltip>\n ))}\n </Stack>\n )}\n </Box>\n )}\n </Box>\n </ListItem>\n {chatErrorReason && (\n <Alert severity={'error'} sx={{ my: 2 }}>\n {chatErrorReason}\n </Alert>\n )}\n </>\n )\n}\n\nexport default SynapseChatInteraction\n"],"mappings":"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"}
1
+ {"version":3,"file":"SynapseChatInteraction.js","names":[],"sources":["../../../src/components/SynapseChat/SynapseChatInteraction.tsx"],"sourcesContent":["import { SynapseSpinner } from '@/components/LoadingScreen/LoadingScreen'\nimport extractMessageFromTraceEvent, {\n TraceMessage,\n} from '@/components/SynapseChat/extractMessageFromTraceEvent'\nimport { KeyboardArrowDown, KeyboardArrowRight } from '@mui/icons-material'\nimport {\n Alert,\n Box,\n Button,\n Chip,\n Collapse,\n ListItem,\n ListItemText,\n Stack,\n Tooltip,\n useTheme,\n} from '@mui/material'\nimport { TraceEvent } from '@sage-bionetworks/synapse-types'\nimport { useEffect, useMemo, useRef, useState } from 'react'\nimport MarkdownSynapse from '../Markdown/MarkdownSynapse'\nimport styles from './SynapseChatInteraction.module.scss'\nimport {\n AttachmentStripItem,\n ChatAttachmentStrip,\n} from './components/ChatAttachmentStrip/ChatAttachmentStrip'\nimport { AgentChatAttachmentStatus } from '@sage-bionetworks/synapse-client'\n\n/**\n * A chat attachment to display on a sent/pending turn. Only `fileHandleId` is guaranteed --\n * `fileName`/`contentType` are omitted for a turn restored from history, where only the\n * fileHandleId that was sent to the server is known, with no in-session upload record to fall\n * back on (see SynapseChatMessage).\n */\nexport type SynapseChatInteractionAttachment = {\n fileHandleId: string\n fileName?: string\n contentType?: string\n}\n\nexport type SynapseChatInteractionProps = {\n userMessage: string\n chatResponseText?: string\n chatResponseTrace?: TraceEvent[]\n scrollIntoView?: boolean\n chatErrorReason?: string\n onSendChat?: (message: string) => void\n agentAvatar: React.ReactNode\n userAvatar: React.ReactNode\n /** Whether to play the fade/rise-in entry animation for this interaction on mount. */\n animateEntry?: boolean\n /** Whether a turn is currently in flight; gates the guide-prompt chips so they can't bypass the composer. */\n isAwaitingResponse?: boolean\n /** Attachments the user sent with this message, if any. */\n attachments?: SynapseChatInteractionAttachment[]\n /** Server-reported outcome of staging each attachment, if available (see AgentChatResponseWithAttachmentStatuses). */\n attachmentStatuses?: AgentChatAttachmentStatus[]\n /** Whether to show the loading spinner icon while the response is in flight. */\n showLoadingIcon?: boolean\n}\n\n// Show tool calls in the trace. Useful for development. We may want to show them to users in the future.\nconst SHOW_TOOL_USE = false\n\nfunction getMarkdownForTraceMessage(traceMessage: TraceMessage): string {\n if ('reasoningText' in traceMessage) {\n return traceMessage.reasoningText\n }\n if (SHOW_TOOL_USE && 'toolName' in traceMessage) {\n return `**Tool Used:** \\`${\n traceMessage.toolName\n }\\`\\n\\n**Tool Input:**\\n\\`\\`\\`json\\n${JSON.stringify(\n traceMessage.toolInput,\n null,\n 2,\n )}\\n\\`\\`\\`\\n`\n }\n return ''\n}\n\nexport function SynapseChatInteraction({\n userMessage,\n chatResponseText,\n chatErrorReason,\n chatResponseTrace,\n scrollIntoView = false,\n onSendChat,\n agentAvatar,\n userAvatar,\n animateEntry = false,\n isAwaitingResponse = false,\n attachments,\n attachmentStatuses,\n showLoadingIcon = true,\n}: SynapseChatInteractionProps) {\n const theme = useTheme()\n const userMessageRef = useRef<HTMLLIElement | null>(null)\n const responseRef = useRef<HTMLLIElement | null>(null)\n const [showTrace, setShowTrace] = useState(false)\n\n const isLoading = !chatResponseText && !chatErrorReason\n\n useEffect(() => {\n // on mount, scroll into view if instructed. This brings the \"Thinking...\" row to the top so\n // the answer's opening lines are usually visible as soon as it starts streaming in.\n if (scrollIntoView) {\n userMessageRef.current?.scrollIntoView({ behavior: 'smooth' })\n }\n }, [scrollIntoView])\n\n useEffect(() => {\n // once the response (or an error) arrives, scroll all the way to the bottom so the full\n // answer - which may be taller than the viewport - is brought into view.\n if (scrollIntoView && !isLoading) {\n responseRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' })\n }\n }, [scrollIntoView, isLoading])\n\n const traceMessages = useMemo(\n () =>\n (chatResponseTrace ?? [])\n .flatMap(traceEvent => extractMessageFromTraceEvent(traceEvent))\n ?.filter(trace => !!trace),\n [chatResponseTrace],\n )\n\n const hasTraceInfo = traceMessages.length > 0\n\n const lastReasoningMessage = useMemo(\n () => traceMessages.filter(trace => 'reasoningText' in trace).at(-1),\n [traceMessages],\n )\n\n const traceButtonLoadingText =\n lastReasoningMessage == null\n ? 'Thinking...'\n : lastReasoningMessage.reasoningText\n\n const traceButtonText = isLoading\n ? traceButtonLoadingText\n : `${showTrace ? 'Hide' : 'Show'} Trace`\n\n const { textContent, guidePrompts } = useMemo(() => {\n try {\n const parser = new DOMParser()\n const doc = parser.parseFromString(chatResponseText ?? '', 'text/html')\n\n // Remove tool_name and actions elements (AI XML tags not for display)\n doc\n .querySelectorAll('tool_name, actions')\n .forEach(element => element.remove())\n\n // Extract guideprompts before removing them\n const extractedGuidePrompts = Array.from(\n doc.querySelectorAll('guideprompt'),\n )\n .map(el => el.textContent?.trim())\n .filter((t): t is string => !!t)\n\n // Remove guideprompt elements\n doc.querySelectorAll('guideprompt').forEach(element => element.remove())\n\n // Extract text from <chat> element if present, otherwise use full body text\n const chatElement = doc.querySelector('chat')\n return {\n textContent: chatElement\n ? (chatElement.textContent ?? '')\n : (doc.body.textContent ?? ''),\n guidePrompts: extractedGuidePrompts,\n }\n } catch (e) {\n console.error(e)\n return { textContent: chatResponseText ?? '', guidePrompts: [] }\n }\n }, [chatResponseText])\n\n const attachmentItems: AttachmentStripItem[] = useMemo(\n () =>\n (attachments ?? []).map(attachment => {\n const status = attachmentStatuses?.find(\n s => s.fileHandleId === attachment.fileHandleId,\n )\n return {\n fileHandleId: attachment.fileHandleId,\n label: attachment.fileName ?? attachment.fileHandleId,\n contentType: attachment.contentType,\n status: status?.status === 'FAILED' ? 'failed' : 'default',\n errorMessage: status?.failureMessage,\n }\n }),\n [attachments, attachmentStatuses],\n )\n\n return (\n <>\n <ListItem\n ref={userMessageRef}\n sx={{\n alignSelf: 'flex-end',\n maxWidth: '82%',\n mb: '5px',\n wordWrap: 'break-word',\n width: 'auto',\n display: 'grid',\n gridTemplateColumns: 'auto auto',\n columnGap: '16px',\n alignItems: 'start',\n justifyItems: 'end',\n }}\n >\n <Box\n className={animateEntry ? styles.entryAnimation : undefined}\n sx={{\n p: '4px 14px',\n backgroundColor: '#F3F6F7',\n borderRadius: '8px',\n }}\n >\n <ListItemText primary={userMessage} />\n </Box>\n <Box sx={{ marginTop: '4px' }}>{userAvatar}</Box>\n </ListItem>\n {attachmentItems.length > 0 && (\n <ListItem sx={{ display: 'block', p: '0px', mb: '5px' }}>\n <ChatAttachmentStrip items={attachmentItems} wrap />\n </ListItem>\n )}\n <ListItem\n ref={responseRef}\n sx={{\n display: 'grid',\n gridTemplateColumns: '50px auto',\n columnGap: '16px',\n justifyItems: 'center',\n alignItems: 'start',\n p: '0px',\n padding: '10px',\n }}\n >\n {agentAvatar}\n <Box\n sx={{\n borderRadius: '10px',\n maxWidth: '100%',\n overflow: 'auto',\n }}\n >\n <Box\n component={'aside'}\n sx={{\n '.markdown': {\n fontStyle: 'italic',\n borderLeft: `4px solid ${theme.palette.grey[300]}`,\n marginLeft: '4px',\n paddingLeft: '8px',\n mt: 1,\n mb: 2,\n },\n }}\n >\n <Button\n variant={'outlined'}\n size={'small'}\n startIcon={\n isLoading ? (\n showLoadingIcon ? (\n <SynapseSpinner size={14} />\n ) : undefined\n ) : showTrace ? (\n <KeyboardArrowDown sx={{ width: '14px' }} />\n ) : (\n <KeyboardArrowRight sx={{ width: '14px' }} />\n )\n }\n disabled={!hasTraceInfo}\n onClick={() => {\n setShowTrace(v => !v)\n }}\n sx={{\n height: '20px',\n fontSize: '12px',\n fontWeight: 600,\n mb: 1,\n border: 'none !important',\n color: 'grey.700',\n justifyContent: 'flex-start',\n whiteSpace: 'nowrap',\n maxWidth: '325px',\n textTransform: 'none',\n pl: 0,\n '.MuiButton-startIcon': { mr: 0.5 },\n }}\n >\n <Box sx={{ overflow: 'hidden', textOverflow: 'ellipsis' }}>\n {traceButtonText}\n </Box>\n </Button>\n {chatResponseTrace && (\n <Collapse in={showTrace}>\n <MarkdownSynapse\n markdown={traceMessages\n .map(getMarkdownForTraceMessage)\n .filter(md => md.trim().length > 0)\n .join('<br/><br/>')}\n />\n </Collapse>\n )}\n </Box>\n {(textContent || (onSendChat && guidePrompts.length > 0)) && (\n <Box className={animateEntry ? styles.entryAnimation : undefined}>\n {textContent && <MarkdownSynapse markdown={textContent} />}\n {onSendChat && guidePrompts.length > 0 && (\n <Stack direction=\"row\" flexWrap=\"wrap\" gap={1} mt={1}>\n {guidePrompts.map(prompt => (\n <Tooltip key={prompt} title={prompt}>\n <Chip\n label={prompt}\n variant=\"outlined\"\n color=\"primary\"\n clickable\n disabled={isAwaitingResponse}\n onClick={() => onSendChat(prompt)}\n sx={{ maxWidth: 200 }}\n />\n </Tooltip>\n ))}\n </Stack>\n )}\n </Box>\n )}\n </Box>\n </ListItem>\n {chatErrorReason && (\n <Alert severity={'error'} sx={{ my: 2 }}>\n {chatErrorReason}\n </Alert>\n )}\n </>\n )\n}\n\nexport default SynapseChatInteraction\n"],"mappings":"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"}