@xinghunm/ai-chat 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -132,15 +132,16 @@ export const CustomChat = () => (
132
132
 
133
133
  一体化 `AiChat` 组件的 Props,继承全部 `AiChatProviderProps`(不含 `children`)。
134
134
 
135
- | 属性 | 类型 | 默认值 | 说明 |
136
- | ------------------------ | --------------- | --------- | ----------------------------------------------------------------------------------------------- |
137
- | `transport` | `ChatTransport` | — | 推荐。由接入方提供的传输适配器。 |
138
- | `apiBaseUrl` | `string` | — | 兼容模式。创建默认内置 transport。 |
139
- | `authToken` | `string` | — | 兼容模式下默认 transport 使用的鉴权头。 |
140
- | `defaultMode` | `ChatAgentMode` | `"agent"` | 新会话的初始 Agent 模式。 |
141
- | `labels` | `AiChatLabels` | — | 可选的 UI 文案覆盖。 |
142
- | `showConversationList` | `boolean` | `false` | 为 `true` 时渲染会话列表侧边栏。 |
143
- | `enableImageAttachments` | `boolean` | `true` | `false` 时隐藏上传按钮、禁用粘贴图片,并使程序化调用 `pickImages`/`pasteImages` 变为 no-op。 |
135
+ | 属性 | 类型 | 默认值 | 说明 |
136
+ | ------------------------ | ------------------------ | ---------------- | --------------------------------------------------------------------------------------------------------- |
137
+ | `transport` | `ChatTransport` | — | 推荐。由接入方提供的传输适配器。 |
138
+ | `apiBaseUrl` | `string` | — | 兼容模式。创建默认内置 transport。 |
139
+ | `authToken` | `string` | — | 兼容模式下默认 transport 使用的鉴权头。 |
140
+ | `defaultMode` | `ChatAgentMode` | `"agent"` | 新会话的初始 Agent 模式。 |
141
+ | `labels` | `AiChatLabels` | — | 可选的 UI 文案覆盖。 |
142
+ | `showConversationList` | `boolean` | `false` | 为 `true` 时渲染会话列表侧边栏。 |
143
+ | `messageRenderOrder` | `ChatMessageRenderOrder` | `"blocks-first"` | 混合纯文本与结构化 block 时的渲染顺序。默认结构化卡片在前;设为 `"timeline"` 时按时间线优先保留文本在前。 |
144
+ | `enableImageAttachments` | `boolean` | `true` | 为 `false` 时隐藏上传按钮、禁用粘贴图片,并使程序化调用 `pickImages`/`pasteImages` 变为 no-op。 |
144
145
 
145
146
  ### `AiChatProviderProps`
146
147
 
@@ -154,6 +155,7 @@ export const CustomChat = () => (
154
155
  | `defaultMode` | `ChatAgentMode` | 否 | 新会话的初始 Agent 模式。 |
155
156
  | `labels` | `AiChatLabels` | 否 | 可选的 UI 文案覆盖。 |
156
157
  | `renderMessageBlock` | `ChatMessageBlockRenderer` | 否 | 自定义消息 block 渲染器,用于承接 `type: "custom"` 等扩展。 |
158
+ | `messageRenderOrder` | `ChatMessageRenderOrder` | 否 | 混合纯文本与结构化 block 时的渲染顺序。默认 `"blocks-first"`,设为 `"timeline"` 可按时间线优先渲染文本片段。 |
157
159
  | `enableImageAttachments` | `boolean` | 否 | 为 `false` 时隐藏上传按钮、禁用粘贴图片,并使程序化调用 `pickImages`/`pasteImages` 变为 no-op。默认 `true`。 |
158
160
  | `children` | `ReactNode` | 是 | Provider 内部渲染的子元素。 |
159
161
 
@@ -167,6 +169,7 @@ export const CustomChat = () => (
167
169
  | `defaultMode` | `ChatAgentMode` | 否 | 新会话的初始 Agent 模式。 |
168
170
  | `labels` | `AiChatLabels` | 否 | 可选的 UI 文案覆盖。 |
169
171
  | `renderMessageBlock` | `ChatMessageBlockRenderer` | 否 | 自定义消息 block 渲染器,用于承接 `type: "custom"` 等扩展。 |
172
+ | `messageRenderOrder` | `ChatMessageRenderOrder` | 否 | 混合纯文本与结构化 block 时的渲染顺序。默认 `"blocks-first"`,设为 `"timeline"` 可按时间线优先渲染文本片段。 |
170
173
  | `enableImageAttachments` | `boolean` | 否 | 为 `false` 时隐藏上传按钮、禁用粘贴图片,并使程序化调用 `pickImages`/`pasteImages` 变为 no-op。默认 `true`。 |
171
174
  | `children` | `ReactNode` | 是 | Provider 内部渲染的子元素。 |
172
175
 
@@ -261,3 +264,49 @@ const renderMessageBlock = ({ block }: ChatMessageBlockRendererProps) => {
261
264
  ```
262
265
 
263
266
  这样业务卡片可以放在接入层或扩展包里,基础包继续只负责通用聊天体验。
267
+
268
+ ## 控制消息渲染顺序
269
+
270
+ 默认情况下,`ai-chat` 采用 `blocks-first` 语义:一条消息同时包含 `content` 和结构化 `blocks` 时,会先渲染卡片类 block,再渲染正文。这适合参数卡、确认卡、结果卡等“先看结构化信息,再看解释文本”的场景。
271
+
272
+ 如果你的接入层会在流式文本中途插入审批卡片、工作流卡片或其他结构化 block,可以把 `messageRenderOrder` 设为 `"timeline"`。这样在消息同时存在纯文本和非 markdown block 时,组件会优先保留文本在前,再接上 block,更接近真实到达顺序。
273
+
274
+ 从当前版本开始,`timeline` 模式还会自动记录结构化 block 第一次出现时对应的文本位置。也就是说:
275
+
276
+ - 当正文已经流出一部分时,后续才收到审批卡或其他自定义 block,`ai-chat` 会把这张卡片锚定在它首次出现的位置。
277
+ - 卡片出现之后继续流出的新文本,会自动渲染到卡片后面。
278
+ - 业务侧不需要再为了“卡片停留在触发点”手动把前文转成 `markdown block`。
279
+
280
+ 这尤其适合下面这种真实流式场景:
281
+
282
+ 1. assistant 先输出一段解释文本
283
+ 2. 中途触发 `approval_required` 或其他自定义卡片
284
+ 3. assistant 在卡片之后继续输出补充说明
285
+
286
+ 在这种情况下,`timeline` 会自动把消息排成 “前文 -> 卡片 -> 后文”。
287
+
288
+ ```tsx
289
+ import { AiChatProvider, ChatThread, ChatComposer } from '@xinghunm/ai-chat'
290
+
291
+ export const CustomChat = () => (
292
+ <AiChatProvider
293
+ transport={transport}
294
+ messageRenderOrder="timeline"
295
+ renderMessageBlock={({ block }) => {
296
+ if (block.type !== 'custom' || block.kind !== 'tool-approval') {
297
+ return null
298
+ }
299
+
300
+ return <ToolApprovalCard data={block.data} />
301
+ }}
302
+ >
303
+ <ChatThread />
304
+ <ChatComposer />
305
+ </AiChatProvider>
306
+ )
307
+ ```
308
+
309
+ 选择建议:
310
+
311
+ - 继续使用默认值 `"blocks-first"`:适合静态消息排版,结构化信息应始终优先展示。
312
+ - 使用 `"timeline"`:适合流式响应中途插卡,希望卡片停留在触发事件位置,且不想在业务侧维护额外文本冻结逻辑的场景。
package/dist/index.d.mts CHANGED
@@ -185,6 +185,11 @@ type ChatMessageBlock = {
185
185
  kind: string;
186
186
  data: unknown;
187
187
  };
188
+ /**
189
+ * Supported message body render orders for mixed text and structured blocks.
190
+ */
191
+ declare const CHAT_MESSAGE_RENDER_ORDERS: readonly ["blocks-first", "timeline"];
192
+ type ChatMessageRenderOrder = (typeof CHAT_MESSAGE_RENDER_ORDERS)[number];
188
193
  /**
189
194
  * Chat message stored in the UI state and rendered by the chat thread.
190
195
  */
@@ -348,6 +353,8 @@ interface AiChatProviderBaseProps {
348
353
  labels?: AiChatLabels;
349
354
  /** Optional renderer used to extend message blocks with app-specific UI. */
350
355
  renderMessageBlock?: ChatMessageBlockRenderer;
356
+ /** Optional render order used for mixed text and structured message blocks. */
357
+ messageRenderOrder?: ChatMessageRenderOrder;
351
358
  /**
352
359
  * Whether to enable image attachment uploads. Defaults to `true`.
353
360
  * Set to `false` to hide the upload button, disable paste-image handling,
@@ -522,6 +529,8 @@ interface ChatContextValue {
522
529
  retryRef: MutableRefObject<() => Promise<void>>;
523
530
  /** Optional block renderer used to extend message rendering with custom block types. */
524
531
  renderMessageBlock?: ChatMessageBlockRenderer;
532
+ /** Optional render order used for mixed text and structured message blocks. */
533
+ messageRenderOrder?: ChatMessageRenderOrder;
525
534
  /** Optional packet transformer used by the legacy default adapter. */
526
535
  transformStreamPacket?: TransformChatStreamPacket;
527
536
  /** Whether image attachments are enabled. Defaults to true. */
@@ -542,4 +551,4 @@ declare const useChatContext: () => ChatContextValue;
542
551
  */
543
552
  declare const useChatStore: <T>(selector: (state: ChatStore) => T) => T;
544
553
 
545
- export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, ChatConversationList, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageStatus, type ChatModel, type ChatRole, type ChatSession, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, ChatThread, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type ExecutionConfirmationSubmission, type ExecutionProposal, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
554
+ export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, ChatConversationList, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatRole, type ChatSession, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, ChatThread, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type ExecutionConfirmationSubmission, type ExecutionProposal, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
package/dist/index.d.ts CHANGED
@@ -185,6 +185,11 @@ type ChatMessageBlock = {
185
185
  kind: string;
186
186
  data: unknown;
187
187
  };
188
+ /**
189
+ * Supported message body render orders for mixed text and structured blocks.
190
+ */
191
+ declare const CHAT_MESSAGE_RENDER_ORDERS: readonly ["blocks-first", "timeline"];
192
+ type ChatMessageRenderOrder = (typeof CHAT_MESSAGE_RENDER_ORDERS)[number];
188
193
  /**
189
194
  * Chat message stored in the UI state and rendered by the chat thread.
190
195
  */
@@ -348,6 +353,8 @@ interface AiChatProviderBaseProps {
348
353
  labels?: AiChatLabels;
349
354
  /** Optional renderer used to extend message blocks with app-specific UI. */
350
355
  renderMessageBlock?: ChatMessageBlockRenderer;
356
+ /** Optional render order used for mixed text and structured message blocks. */
357
+ messageRenderOrder?: ChatMessageRenderOrder;
351
358
  /**
352
359
  * Whether to enable image attachment uploads. Defaults to `true`.
353
360
  * Set to `false` to hide the upload button, disable paste-image handling,
@@ -522,6 +529,8 @@ interface ChatContextValue {
522
529
  retryRef: MutableRefObject<() => Promise<void>>;
523
530
  /** Optional block renderer used to extend message rendering with custom block types. */
524
531
  renderMessageBlock?: ChatMessageBlockRenderer;
532
+ /** Optional render order used for mixed text and structured message blocks. */
533
+ messageRenderOrder?: ChatMessageRenderOrder;
525
534
  /** Optional packet transformer used by the legacy default adapter. */
526
535
  transformStreamPacket?: TransformChatStreamPacket;
527
536
  /** Whether image attachments are enabled. Defaults to true. */
@@ -542,4 +551,4 @@ declare const useChatContext: () => ChatContextValue;
542
551
  */
543
552
  declare const useChatStore: <T>(selector: (state: ChatStore) => T) => T;
544
553
 
545
- export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, ChatConversationList, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageStatus, type ChatModel, type ChatRole, type ChatSession, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, ChatThread, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type ExecutionConfirmationSubmission, type ExecutionProposal, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };
554
+ export { AiChat, type AiChatLabels, type AiChatProps, AiChatProvider, type AiChatProviderProps, CHAT_AGENT_MODES, CHAT_MESSAGE_RENDER_ORDERS, type ChatAgentMode, ChatComposer, type ChatComposerViewProps, ChatConversationList, type ChatImageAttachment, type ChatMessage, type ChatMessageBlock, type ChatMessageBlockRenderer, type ChatMessageBlockRendererProps, type ChatMessageRenderOrder, type ChatMessageStatus, type ChatModel, type ChatRole, type ChatSession, type ChatStreamMessagePatch, type ChatStreamPacketTransformArgs, type ChatStreamPacketUpdate, ChatThread, type ChatTransport, type ChatTransportStartStreamArgs, type CreateDefaultChatTransportOptions, DEFAULT_AI_CHAT_LABELS, DEFAULT_CHAT_AGENT_MODE, type DefaultChatTransportEndpoints, type ExecutionConfirmationSubmission, type ExecutionProposal, type PlanQuestionnaire, type PlanQuestionnaireSubmission, type ResultSummary, type TransformChatStreamPacket, createDefaultChatTransport, useChatContext, useChatStore };