UNPKG

@assistant-ui/react

Version:

Open-source TypeScript/React library for building production-grade AI chat experiences

337 lines (336 loc) • 10.4 kB
"use client"; import { PartByIndexProvider } from "../../context/providers/PartByIndexProvider.js"; import { TextMessagePartProvider } from "../../context/providers/TextMessagePartProvider.js"; import { MessagePartPrimitiveText } from "../messagePart/MessagePartText.js"; import { MessagePartPrimitiveImage } from "../messagePart/MessagePartImage.js"; import { MessagePartPrimitiveInProgress } from "../messagePart/MessagePartInProgress.js"; import { getMessagePartKeys } from "@assistant-ui/core/internal"; import { c } from "@assistant-ui/tap/react-shim/compiler-runtime"; import { MessagePartComponent } from "@assistant-ui/core/react"; import { memo } from "@assistant-ui/tap/react-shim"; import { useAuiState } from "@assistant-ui/store"; import { Fragment, jsx, jsxs } from "react/jsx-runtime"; // src/primitives/message/MessagePartsGrouped.tsx /** * Groups message parts by their parent ID. * Parts without a parent ID appear in their chronological position as individual groups. * Parts with the same parent ID are grouped together at the position of their first occurrence. */ const groupMessagePartsByParentId = (parts) => { const groupMap = /* @__PURE__ */ new Map(); for (let i = 0; i < parts.length; i++) { const groupId = parts[i]?.parentId ?? i; const indices = groupMap.get(groupId) ?? []; indices.push(i); groupMap.set(groupId, indices); } const groups = []; for (const [groupId, indices] of groupMap) { const groupKey = typeof groupId === "string" ? groupId : void 0; groups.push({ groupKey, indices }); } return groups; }; const useMessagePartsGrouped = (groupingFunction) => { const $ = c(9); const parts = useAuiState(_temp); let t0; bb0: { if (parts.length === 0) { let t1; if ($[0] === Symbol.for("react.memo_cache_sentinel")) { t1 = []; $[0] = t1; } else t1 = $[0]; t0 = t1; break bb0; } let t1; if ($[1] !== groupingFunction || $[2] !== parts) { t1 = groupingFunction(parts); $[1] = groupingFunction; $[2] = parts; $[3] = t1; } else t1 = $[3]; t0 = t1; } const groups = t0; let t1; if ($[4] !== parts) { t1 = getMessagePartKeys(parts); $[4] = parts; $[5] = t1; } else t1 = $[5]; let t2; if ($[6] !== groups || $[7] !== t1) { t2 = { groups, partKeys: t1 }; $[6] = groups; $[7] = t1; $[8] = t2; } else t2 = $[8]; return t2; }; const defaultComponents = { Text: () => /* @__PURE__ */ jsxs("p", { style: { whiteSpace: "pre-line" }, children: [/* @__PURE__ */ jsx(MessagePartPrimitiveText, {}), /* @__PURE__ */ jsx(MessagePartPrimitiveInProgress, { children: /* @__PURE__ */ jsx("span", { style: { fontFamily: "revert" }, children: " ●" }) })] }), Reasoning: () => null, Source: () => null, Image: () => /* @__PURE__ */ jsx(MessagePartPrimitiveImage, {}), File: () => null, Unstable_Audio: () => null, Group: ({ children }) => children }; const MessagePart = memo((t0) => { const $ = c(5); const { partIndex, components } = t0; let t1; if ($[0] !== components) { t1 = /* @__PURE__ */ jsx(MessagePartComponent, { components }); $[0] = components; $[1] = t1; } else t1 = $[1]; let t2; if ($[2] !== partIndex || $[3] !== t1) { t2 = /* @__PURE__ */ jsx(PartByIndexProvider, { index: partIndex, children: t1 }); $[2] = partIndex; $[3] = t1; $[4] = t2; } else t2 = $[4]; return t2; }, (prev, next) => prev.partIndex === next.partIndex && prev.components?.Text === next.components?.Text && prev.components?.Reasoning === next.components?.Reasoning && prev.components?.Source === next.components?.Source && prev.components?.Image === next.components?.Image && prev.components?.File === next.components?.File && prev.components?.Unstable_Audio === next.components?.Unstable_Audio && prev.components?.tools === next.components?.tools && prev.components?.data === next.components?.data); const EmptyPartFallback = (t0) => { const $ = c(6); const { status, component: Component } = t0; const t1 = status.type === "running"; let t2; if ($[0] !== Component || $[1] !== status) { t2 = /* @__PURE__ */ jsx(Component, { type: "text", text: "", status }); $[0] = Component; $[1] = status; $[2] = t2; } else t2 = $[2]; let t3; if ($[3] !== t1 || $[4] !== t2) { t3 = /* @__PURE__ */ jsx(TextMessagePartProvider, { text: "", isRunning: t1, children: t2 }); $[3] = t1; $[4] = t2; $[5] = t3; } else t3 = $[5]; return t3; }; const COMPLETE_STATUS = Object.freeze({ type: "complete" }); const EmptyPartsImpl = (t0) => { const $ = c(6); const { components } = t0; const status = useAuiState(_temp2); if (components?.Empty) { let t1; if ($[0] !== components.Empty || $[1] !== status) { t1 = /* @__PURE__ */ jsx(components.Empty, { status }); $[0] = components.Empty; $[1] = status; $[2] = t1; } else t1 = $[2]; return t1; } const t1 = components?.Text ?? defaultComponents.Text; let t2; if ($[3] !== status || $[4] !== t1) { t2 = /* @__PURE__ */ jsx(EmptyPartFallback, { status, component: t1 }); $[3] = status; $[4] = t1; $[5] = t2; } else t2 = $[5]; return t2; }; const EmptyParts = memo(EmptyPartsImpl, (prev, next) => prev.components?.Empty === next.components?.Empty && prev.components?.Text === next.components?.Text); /** * Renders the parts of a message grouped by a custom grouping function. * * This component allows you to group message parts based on any criteria you define. * The grouping function receives all message parts and returns an array of groups, * where each group has a key and an array of part indices. * * @deprecated Prefer `<MessagePrimitive.GroupedParts>` for adjacent * grouping — it dispatches all rendering through one `switch (part.type)` * and supports nested group paths. Keep this primitive only for * non-adjacent clustering (e.g., gathering parts with the same parent-id * across the message). * * @example * ```tsx * // Group by parent ID (default behavior) * <MessagePrimitive.Unstable_PartsGrouped * components={{ * Text: ({ text }) => <p className="message-text">{text}</p>, * Image: ({ image }) => <img src={image} alt="Message image" />, * Group: ({ groupKey, indices, children }) => { * if (!groupKey) return <>{children}</>; * return ( * <div className="parent-group border rounded p-4"> * <h4>Parent ID: {groupKey}</h4> * {children} * </div> * ); * } * }} * /> * ``` */ const MessagePrimitiveUnstable_PartsGrouped = (t0) => { const $ = c(21); const { groupingFunction, components } = t0; const contentLength = useAuiState(_temp3); const { groups: messageGroups, partKeys } = useMessagePartsGrouped(groupingFunction); let t1; bb0: { if (contentLength === 0) { let t2; if ($[0] !== components) { t2 = /* @__PURE__ */ jsx(EmptyParts, { components }); $[0] = components; $[1] = t2; } else t2 = $[1]; t1 = t2; break bb0; } const t2 = components?.Text ?? defaultComponents.Text; const t3 = components?.Reasoning ?? defaultComponents.Reasoning; const t4 = components?.Image ?? defaultComponents.Image; const t5 = components?.Source ?? defaultComponents.Source; const t6 = components?.File ?? defaultComponents.File; const t7 = components?.Unstable_Audio ?? defaultComponents.Unstable_Audio; let t8; if ($[2] !== components || $[3] !== t2 || $[4] !== t3 || $[5] !== t4 || $[6] !== t5 || $[7] !== t6 || $[8] !== t7) { t8 = { ...components, Text: t2, Reasoning: t3, Image: t4, Source: t5, File: t6, Unstable_Audio: t7 }; $[2] = components; $[3] = t2; $[4] = t3; $[5] = t4; $[6] = t5; $[7] = t6; $[8] = t7; $[9] = t8; } else t8 = $[9]; const leafComponents = t8; let t9; if ($[10] !== components?.Group || $[11] !== leafComponents || $[12] !== messageGroups || $[13] !== partKeys) { let t10; if ($[15] !== components?.Group || $[16] !== leafComponents || $[17] !== partKeys) { t10 = (group, groupIndex) => { const GroupComponent = components?.Group ?? defaultComponents.Group; const identity = partKeys[group.indices[0]]; return /* @__PURE__ */ jsx(GroupComponent, { groupKey: group.groupKey, indices: group.indices, children: group.indices.map((partIndex) => /* @__PURE__ */ jsx(MessagePart, { partIndex, components: leafComponents }, partKeys[partIndex])) }, JSON.stringify([ "group", group.groupKey ?? null, identity?.includes(":") ? `id:${identity}` : groupIndex ])); }; $[15] = components?.Group; $[16] = leafComponents; $[17] = partKeys; $[18] = t10; } else t10 = $[18]; t9 = messageGroups.map(t10); $[10] = components?.Group; $[11] = leafComponents; $[12] = messageGroups; $[13] = partKeys; $[14] = t9; } else t9 = $[14]; t1 = t9; } const partsElements = t1; let t2; if ($[19] !== partsElements) { t2 = /* @__PURE__ */ jsx(Fragment, { children: partsElements }); $[19] = partsElements; $[20] = t2; } else t2 = $[20]; return t2; }; MessagePrimitiveUnstable_PartsGrouped.displayName = "MessagePrimitive.Unstable_PartsGrouped"; /** * Renders the parts of a message grouped by their parent ID. * This is a convenience wrapper around Unstable_PartsGrouped with parent ID grouping. * * @deprecated Use MessagePrimitive.Unstable_PartsGrouped instead for more flexibility */ const MessagePrimitiveUnstable_PartsGroupedByParentId = (t0) => { const $ = c(6); let components; let props; if ($[0] !== t0) { ({components, ...props} = t0); $[0] = t0; $[1] = components; $[2] = props; } else { components = $[1]; props = $[2]; } let t1; if ($[3] !== components || $[4] !== props) { t1 = /* @__PURE__ */ jsx(MessagePrimitiveUnstable_PartsGrouped, { ...props, components, groupingFunction: groupMessagePartsByParentId }); $[3] = components; $[4] = props; $[5] = t1; } else t1 = $[5]; return t1; }; MessagePrimitiveUnstable_PartsGroupedByParentId.displayName = "MessagePrimitive.Unstable_PartsGroupedByParentId"; function _temp(s) { return s.message.parts; } function _temp2(s) { return s.message.status ?? COMPLETE_STATUS; } function _temp3(s) { return s.message.parts.length; } export { MessagePrimitiveUnstable_PartsGrouped, MessagePrimitiveUnstable_PartsGroupedByParentId }; //# sourceMappingURL=MessagePartsGrouped.js.map