@xinghunm/ai-chat 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -102,11 +102,28 @@ interface PlanBooleanQuestion extends PlanQuestionBase {
102
102
  */
103
103
  type PlanQuestion = PlanSingleSelectQuestion | PlanMultiSelectQuestion | PlanTextQuestion | PlanNumberQuestion | PlanBooleanQuestion;
104
104
  type PlanQuestionnaireStatus = 'expired' | 'failed';
105
+ /**
106
+ * Merge behavior applied when a keyed structured block is patched repeatedly.
107
+ */
108
+ type ChatBlockMergePolicy = 'append' | 'replace' | 'ignore-duplicate';
105
109
  /**
106
110
  * Structured question form emitted for plan-mode clarification flows.
107
111
  */
108
112
  interface PlanQuestionnaire {
109
113
  questionnaireId: string;
114
+ /**
115
+ * Stable key used to identify the same logical questionnaire across streaming patches.
116
+ * When present, questionnaires are merged by `blockKey` instead of `questionnaireId`.
117
+ */
118
+ blockKey?: string;
119
+ /**
120
+ * Merge strategy used when another questionnaire with the same key arrives.
121
+ *
122
+ * - `append`: keep appending questionnaires even if the key matches.
123
+ * - `replace`: replace the existing keyed questionnaire with the latest one.
124
+ * - `ignore-duplicate`: keep the first keyed questionnaire and ignore later duplicates.
125
+ */
126
+ mergePolicy?: ChatBlockMergePolicy;
110
127
  title?: string;
111
128
  description?: string;
112
129
  submitLabel?: string;
@@ -120,6 +137,10 @@ interface PlanQuestionnaire {
120
137
  */
121
138
  interface PlanQuestionnaireSubmission {
122
139
  questionnaireId: string;
140
+ /**
141
+ * Stable questionnaire block key forwarded from the rendered card when available.
142
+ */
143
+ blockKey?: string;
123
144
  answers: Record<string, PlanQuestionnaireAnswerValue>;
124
145
  content: string;
125
146
  sourceMessageId?: string;
@@ -168,10 +189,6 @@ interface ResultSummary {
168
189
  headline: string;
169
190
  details: string[];
170
191
  }
171
- /**
172
- * Merge behavior applied when a keyed custom block is patched repeatedly.
173
- */
174
- type ChatCustomBlockMergePolicy = 'append' | 'replace' | 'ignore-duplicate';
175
192
  /**
176
193
  * Extensible custom block rendered by a consumer-provided block renderer.
177
194
  */
@@ -191,7 +208,7 @@ interface ChatCustomBlock {
191
208
  * - `replace`: replace the existing keyed block with the latest one.
192
209
  * - `ignore-duplicate`: keep the first keyed block and ignore later duplicates.
193
210
  */
194
- mergePolicy?: ChatCustomBlockMergePolicy;
211
+ mergePolicy?: ChatBlockMergePolicy;
195
212
  }
196
213
  /**
197
214
  * Structured assistant block variants rendered by the chat thread.
package/dist/index.d.ts CHANGED
@@ -102,11 +102,28 @@ interface PlanBooleanQuestion extends PlanQuestionBase {
102
102
  */
103
103
  type PlanQuestion = PlanSingleSelectQuestion | PlanMultiSelectQuestion | PlanTextQuestion | PlanNumberQuestion | PlanBooleanQuestion;
104
104
  type PlanQuestionnaireStatus = 'expired' | 'failed';
105
+ /**
106
+ * Merge behavior applied when a keyed structured block is patched repeatedly.
107
+ */
108
+ type ChatBlockMergePolicy = 'append' | 'replace' | 'ignore-duplicate';
105
109
  /**
106
110
  * Structured question form emitted for plan-mode clarification flows.
107
111
  */
108
112
  interface PlanQuestionnaire {
109
113
  questionnaireId: string;
114
+ /**
115
+ * Stable key used to identify the same logical questionnaire across streaming patches.
116
+ * When present, questionnaires are merged by `blockKey` instead of `questionnaireId`.
117
+ */
118
+ blockKey?: string;
119
+ /**
120
+ * Merge strategy used when another questionnaire with the same key arrives.
121
+ *
122
+ * - `append`: keep appending questionnaires even if the key matches.
123
+ * - `replace`: replace the existing keyed questionnaire with the latest one.
124
+ * - `ignore-duplicate`: keep the first keyed questionnaire and ignore later duplicates.
125
+ */
126
+ mergePolicy?: ChatBlockMergePolicy;
110
127
  title?: string;
111
128
  description?: string;
112
129
  submitLabel?: string;
@@ -120,6 +137,10 @@ interface PlanQuestionnaire {
120
137
  */
121
138
  interface PlanQuestionnaireSubmission {
122
139
  questionnaireId: string;
140
+ /**
141
+ * Stable questionnaire block key forwarded from the rendered card when available.
142
+ */
143
+ blockKey?: string;
123
144
  answers: Record<string, PlanQuestionnaireAnswerValue>;
124
145
  content: string;
125
146
  sourceMessageId?: string;
@@ -168,10 +189,6 @@ interface ResultSummary {
168
189
  headline: string;
169
190
  details: string[];
170
191
  }
171
- /**
172
- * Merge behavior applied when a keyed custom block is patched repeatedly.
173
- */
174
- type ChatCustomBlockMergePolicy = 'append' | 'replace' | 'ignore-duplicate';
175
192
  /**
176
193
  * Extensible custom block rendered by a consumer-provided block renderer.
177
194
  */
@@ -191,7 +208,7 @@ interface ChatCustomBlock {
191
208
  * - `replace`: replace the existing keyed block with the latest one.
192
209
  * - `ignore-duplicate`: keep the first keyed block and ignore later duplicates.
193
210
  */
194
- mergePolicy?: ChatCustomBlockMergePolicy;
211
+ mergePolicy?: ChatBlockMergePolicy;
195
212
  }
196
213
  /**
197
214
  * Structured assistant block variants rendered by the chat thread.
package/dist/index.js CHANGED
@@ -124,6 +124,22 @@ var mergeStreamingBlocks = (existingBlocks, incomingBlocks) => {
124
124
  nextBlocks.push(incomingBlock);
125
125
  return;
126
126
  }
127
+ if (incomingBlock.type === "questionnaire" && incomingBlock.questionnaire.blockKey) {
128
+ const mergePolicy = incomingBlock.questionnaire.mergePolicy ?? "append";
129
+ if (mergePolicy !== "append") {
130
+ const existingIndex2 = nextBlocks.findIndex(
131
+ (block) => block.type === "questionnaire" && block.questionnaire.blockKey === incomingBlock.questionnaire.blockKey
132
+ );
133
+ if (existingIndex2 !== -1) {
134
+ if (mergePolicy === "replace") {
135
+ nextBlocks[existingIndex2] = incomingBlock;
136
+ }
137
+ return;
138
+ }
139
+ }
140
+ nextBlocks.push(incomingBlock);
141
+ return;
142
+ }
127
143
  if (incomingBlock.type !== "questionnaire") {
128
144
  nextBlocks.push(incomingBlock);
129
145
  return;
@@ -1024,7 +1040,7 @@ var getTimelineBlockKey = (block, index) => {
1024
1040
  case "result_summary":
1025
1041
  return `${index}:result_summary:${block.summary.summaryId}:${block.summary.status}`;
1026
1042
  case "questionnaire":
1027
- return `${index}:questionnaire:${block.questionnaire.questionnaireId}`;
1043
+ return block.questionnaire.blockKey ? `questionnaire:${block.questionnaire.blockKey}` : `${index}:questionnaire:${block.questionnaire.questionnaireId}`;
1028
1044
  case "custom":
1029
1045
  return block.blockKey ? `custom:${block.blockKey}` : `${index}:custom:${block.kind}:${stringifyTimelineKeyPart(block.data)}`;
1030
1046
  default:
@@ -1672,6 +1688,7 @@ var QuestionnaireCardInner = ({
1672
1688
  try {
1673
1689
  await onSubmit?.({
1674
1690
  questionnaireId: questionnaire.questionnaireId,
1691
+ ...questionnaire.blockKey ? { blockKey: questionnaire.blockKey } : {},
1675
1692
  answers: normalizedAnswers,
1676
1693
  content: contentLines.join("\n")
1677
1694
  });
@@ -2483,7 +2500,7 @@ var ChatMessageItemView = ({
2483
2500
  sourceMessageId: message.id
2484
2501
  }) : void 0
2485
2502
  }
2486
- ) }, `questionnaire-${index}`);
2503
+ ) }, block.questionnaire.blockKey ?? `questionnaire-${index}`);
2487
2504
  case "custom":
2488
2505
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(import_react9.Fragment, { children: renderMessageBlock?.({
2489
2506
  block,
package/dist/index.mjs CHANGED
@@ -77,6 +77,22 @@ var mergeStreamingBlocks = (existingBlocks, incomingBlocks) => {
77
77
  nextBlocks.push(incomingBlock);
78
78
  return;
79
79
  }
80
+ if (incomingBlock.type === "questionnaire" && incomingBlock.questionnaire.blockKey) {
81
+ const mergePolicy = incomingBlock.questionnaire.mergePolicy ?? "append";
82
+ if (mergePolicy !== "append") {
83
+ const existingIndex2 = nextBlocks.findIndex(
84
+ (block) => block.type === "questionnaire" && block.questionnaire.blockKey === incomingBlock.questionnaire.blockKey
85
+ );
86
+ if (existingIndex2 !== -1) {
87
+ if (mergePolicy === "replace") {
88
+ nextBlocks[existingIndex2] = incomingBlock;
89
+ }
90
+ return;
91
+ }
92
+ }
93
+ nextBlocks.push(incomingBlock);
94
+ return;
95
+ }
80
96
  if (incomingBlock.type !== "questionnaire") {
81
97
  nextBlocks.push(incomingBlock);
82
98
  return;
@@ -977,7 +993,7 @@ var getTimelineBlockKey = (block, index) => {
977
993
  case "result_summary":
978
994
  return `${index}:result_summary:${block.summary.summaryId}:${block.summary.status}`;
979
995
  case "questionnaire":
980
- return `${index}:questionnaire:${block.questionnaire.questionnaireId}`;
996
+ return block.questionnaire.blockKey ? `questionnaire:${block.questionnaire.blockKey}` : `${index}:questionnaire:${block.questionnaire.questionnaireId}`;
981
997
  case "custom":
982
998
  return block.blockKey ? `custom:${block.blockKey}` : `${index}:custom:${block.kind}:${stringifyTimelineKeyPart(block.data)}`;
983
999
  default:
@@ -1625,6 +1641,7 @@ var QuestionnaireCardInner = ({
1625
1641
  try {
1626
1642
  await onSubmit?.({
1627
1643
  questionnaireId: questionnaire.questionnaireId,
1644
+ ...questionnaire.blockKey ? { blockKey: questionnaire.blockKey } : {},
1628
1645
  answers: normalizedAnswers,
1629
1646
  content: contentLines.join("\n")
1630
1647
  });
@@ -2436,7 +2453,7 @@ var ChatMessageItemView = ({
2436
2453
  sourceMessageId: message.id
2437
2454
  }) : void 0
2438
2455
  }
2439
- ) }, `questionnaire-${index}`);
2456
+ ) }, block.questionnaire.blockKey ?? `questionnaire-${index}`);
2440
2457
  case "custom":
2441
2458
  return /* @__PURE__ */ jsx8(Fragment, { children: renderMessageBlock?.({
2442
2459
  block,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xinghunm/ai-chat",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "AI chat React component library",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",