@springbrand/message-panel 0.1.3-alpha.8 → 0.2.0-alpha.37

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 (67) hide show
  1. package/cloud-os/README.md +4 -3
  2. package/cloud-os/assets/followup-arrow.svg +3 -0
  3. package/cloud-os/assets/loading-corner.svg +3 -0
  4. package/cloud-os/assets/loading-mark.svg +16 -0
  5. package/cloud-os/assets/loading-spark.svg +3 -0
  6. package/cloud-os/assets/model-selected.svg +5 -0
  7. package/cloud-os/assets/thinking-star.svg +9 -0
  8. package/cloud-os/capability-chip.tsx +1 -1
  9. package/cloud-os/chat/cloud-os-chat-messages.tsx +441 -98
  10. package/cloud-os/chat/code-runner.tsx +106 -0
  11. package/cloud-os/chat/image-generation.tsx +76 -0
  12. package/cloud-os/chat/loading-state.tsx +25 -0
  13. package/cloud-os/chat/markdown-message.tsx +144 -41
  14. package/cloud-os/chat/range.ts +8 -0
  15. package/cloud-os/chat/rich-blocks.tsx +285 -227
  16. package/cloud-os/chat/surfaces.tsx +90 -0
  17. package/cloud-os/chat/tool-call.tsx +94 -0
  18. package/cloud-os/chat/tool-presentation.ts +31 -22
  19. package/cloud-os/chat/tool-rows.tsx +195 -143
  20. package/cloud-os/chat/tool-timeline.tsx +123 -0
  21. package/cloud-os/chat/transcript-model.ts +258 -20
  22. package/cloud-os/chat/web-search.tsx +135 -0
  23. package/cloud-os/composer/cloud-os-chat-input.tsx +38 -88
  24. package/cloud-os/composer/cloud-os-model-select.tsx +105 -0
  25. package/cloud-os/file-view.tsx +195 -12
  26. package/cloud-os/index.ts +24 -3
  27. package/cloud-os/layout/cloud-os-workspace-split.tsx +60 -3
  28. package/cloud-os/primitives/collapsible.tsx +21 -0
  29. package/cloud-os/primitives/workshop-controls.tsx +2 -2
  30. package/cloud-os/styles/cloud-os.css +132 -1
  31. package/demo/camel-chat-showcase.tsx +21 -13
  32. package/demo/chat-scenarios.ts +214 -40
  33. package/demo/cloud-os-chat-showcase.tsx +37 -14
  34. package/demo/fixtures.ts +4 -5
  35. package/demo/message-panel-gallery.tsx +16 -2
  36. package/package.json +8 -4
  37. package/springbrand-pet/LICENSE.bloub +21 -0
  38. package/springbrand-pet/bot/cycles.test.ts +221 -0
  39. package/springbrand-pet/bot/cycles.ts +225 -0
  40. package/springbrand-pet/bot/decor.ts +285 -0
  41. package/springbrand-pet/bot/engine.test.ts +543 -0
  42. package/springbrand-pet/bot/engine.ts +558 -0
  43. package/springbrand-pet/bot/expressions.test.ts +135 -0
  44. package/springbrand-pet/bot/expressions.ts +192 -0
  45. package/springbrand-pet/bot/eyefit.ts +455 -0
  46. package/springbrand-pet/bot/face.test.ts +101 -0
  47. package/springbrand-pet/bot/face.ts +179 -0
  48. package/springbrand-pet/bot/math.ts +42 -0
  49. package/springbrand-pet/bot/profiles.ts +22 -0
  50. package/springbrand-pet/bot/repere.ts +31 -0
  51. package/springbrand-pet/bot/shape.test.ts +97 -0
  52. package/springbrand-pet/bot/shape.ts +310 -0
  53. package/springbrand-pet/bot/skins.test.ts +325 -0
  54. package/springbrand-pet/bot/skins.ts +161 -0
  55. package/springbrand-pet/bot/states.ts +616 -0
  56. package/springbrand-pet/drag.test.ts +19 -0
  57. package/springbrand-pet/index.tsx +569 -0
  58. package/springbrand-pet/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  59. package/springbrand-pet/vitest.config.ts +9 -0
  60. package/src/camel/camel-chat-messages.tsx +78 -78
  61. package/src/camel/camel-tool-presentation.tsx +19 -15
  62. package/src/camel/camel-turn.ts +1 -17
  63. package/src/composer/composer-attachments.tsx +1 -1
  64. package/src/composer/composer.tsx +2 -2
  65. package/src/contracts.ts +0 -2
  66. package/src/message-panel.tsx +1 -24
  67. package/src/parts/index.tsx +103 -64
@@ -167,7 +167,7 @@ export function CamelChatError({
167
167
  }
168
168
 
169
169
  export function CamelChatRecovery({
170
- label = "模型响应暂时中断,正在重试…",
170
+ label = "The model response was interrupted. Retrying…",
171
171
  }: {
172
172
  label?: string;
173
173
  }) {
@@ -668,53 +668,30 @@ function CamelSchedule({
668
668
 
669
669
  function CamelAskUser({
670
670
  part,
671
- submittedAnswer,
672
671
  onAnswer,
673
672
  }: {
674
673
  part: unknown;
675
- submittedAnswer?: string;
676
674
  onAnswer: (payload: unknown) => void;
677
675
  }) {
678
676
  const record = recordOf(part);
679
677
  const data = dataOf(part);
680
- const output = record.output;
681
678
  const questions = Array.isArray(data.questions)
682
679
  ? data.questions.map(recordOf)
683
- : typeof data.question === "string"
684
- ? [{
685
- prompt: data.question,
686
- kind: data.multiSelect === true ? "multi" : "single",
687
- options: Array.isArray(data.options)
688
- ? data.options.filter(
689
- (option): option is string => typeof option === "string",
690
- )
691
- : [],
692
- }]
693
- : [];
694
- const [selections, setSelections] = useState<string[][]>(() =>
695
- questions.map(() => []),
696
- );
697
- const [text, setText] = useState("");
698
- const [submitted, setSubmitted] = useState(false);
699
- const outputRecord = recordOf(output);
700
- const externalAnswer =
701
- submittedAnswer?.trim() ||
702
- (typeof output === "string"
703
- ? output.trim()
704
- : typeof outputRecord.text === "string"
705
- ? outputRecord.text.trim()
706
- : "");
707
- const externalSelections = new Set(
708
- externalAnswer
709
- .split("、")
710
- .map((value) => value.trim())
711
- .filter(Boolean),
680
+ : [];
681
+ const output = recordOf(record.output);
682
+ const settledAnswers = Array.isArray(output.answers)
683
+ ? output.answers.map(recordOf)
684
+ : [];
685
+ const [answers, setAnswers] = useState<Array<{
686
+ selections: string[];
687
+ text: string;
688
+ }>>([]);
689
+ const answerAt = (index: number) =>
690
+ answers[index] ?? { selections: [], text: "" };
691
+ const resolvedSubmitted = settledAnswers.length > 0;
692
+ const valid = questions.length > 0 && questions.every((_, index) =>
693
+ answerAt(index).selections.length > 0 || answerAt(index).text.trim()
712
694
  );
713
- const resolvedSubmitted = submitted || Boolean(externalAnswer);
714
- const valid = questions.every((question, index) => {
715
- const options = Array.isArray(question.options) ? question.options : [];
716
- return options.length === 0 || (selections[index]?.length ?? 0) > 0;
717
- });
718
695
 
719
696
  return (
720
697
  <section
@@ -723,21 +700,28 @@ function CamelAskUser({
723
700
  data-state={resolvedSubmitted ? "submitted" : "idle"}
724
701
  >
725
702
  {questions.map((question, questionIndex) => {
726
- const prompt = String(question.prompt ?? "Choose an option");
703
+ const prompt = String(question.question ?? "Choose an option");
727
704
  const options = Array.isArray(question.options)
728
705
  ? question.options.filter(
729
706
  (option): option is string => typeof option === "string",
730
707
  )
731
708
  : [];
732
- const multi = question.kind === "multi";
709
+ const multi = question.multiSelect === true;
710
+ const allowCustom = question.allowCustom === true || options.length === 0;
711
+ const settledAnswer = settledAnswers[questionIndex];
712
+ const settledSelections = Array.isArray(settledAnswer?.selections)
713
+ ? settledAnswer.selections.filter(
714
+ (value): value is string => typeof value === "string",
715
+ )
716
+ : [];
733
717
  return (
734
718
  <div className="space-y-2" key={`${prompt}:${questionIndex}`}>
735
719
  <p className="text-sm text-foreground">{prompt}</p>
736
720
  <div className="flex flex-wrap gap-2">
737
721
  {options.map((option) => {
738
- const selected = externalAnswer
739
- ? externalSelections.has(option)
740
- : selections[questionIndex]?.includes(option);
722
+ const selected = settledAnswer
723
+ ? settledSelections.includes(option)
724
+ : answerAt(questionIndex).selections.includes(option);
741
725
  return (
742
726
  <Button
743
727
  key={option}
@@ -746,15 +730,21 @@ function CamelAskUser({
746
730
  variant={selected ? "default" : "outline"}
747
731
  disabled={resolvedSubmitted}
748
732
  onClick={() =>
749
- setSelections((current) =>
750
- current.map((values, index) => {
751
- if (index !== questionIndex) return values;
752
- if (!multi) return [option];
753
- return values.includes(option)
754
- ? values.filter((value) => value !== option)
755
- : [...values, option];
756
- }),
757
- )
733
+ setAnswers((current) => {
734
+ const next = questions.map((_, index) =>
735
+ current[index] ?? { selections: [], text: "" }
736
+ );
737
+ const answer = next[questionIndex]!;
738
+ next[questionIndex] = {
739
+ ...answer,
740
+ selections: !multi
741
+ ? [option]
742
+ : answer.selections.includes(option)
743
+ ? answer.selections.filter((value) => value !== option)
744
+ : [...answer.selections, option],
745
+ };
746
+ return next;
747
+ })
758
748
  }
759
749
  >
760
750
  {option}
@@ -762,34 +752,54 @@ function CamelAskUser({
762
752
  );
763
753
  })}
764
754
  </div>
755
+ {!resolvedSubmitted && allowCustom && (
756
+ <input
757
+ value={answerAt(questionIndex).text}
758
+ maxLength={2_000}
759
+ aria-label={`Custom answer: ${prompt}`}
760
+ onChange={(event) =>
761
+ setAnswers((current) => {
762
+ const next = questions.map((_, index) =>
763
+ current[index] ?? { selections: [], text: "" }
764
+ );
765
+ next[questionIndex] = {
766
+ ...next[questionIndex]!,
767
+ text: event.currentTarget.value,
768
+ };
769
+ return next;
770
+ })}
771
+ placeholder={options.length > 0 ? "Other" : "Type your answer"}
772
+ className="h-8 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring focus:ring-2 focus:ring-ring/30"
773
+ />
774
+ )}
775
+ {settledAnswer && (
776
+ <p className="text-xs text-muted-foreground">
777
+ Answered: {[
778
+ ...settledSelections,
779
+ ...(typeof settledAnswer.text === "string" && settledAnswer.text.trim()
780
+ ? [settledAnswer.text.trim()]
781
+ : []),
782
+ ].join(" / ")}
783
+ </p>
784
+ )}
765
785
  </div>
766
786
  );
767
787
  })}
768
- {typeof data.freeTextPlaceholder === "string" && (
769
- <input
770
- value={text}
771
- disabled={resolvedSubmitted}
772
- onChange={(event) => setText(event.currentTarget.value)}
773
- placeholder={data.freeTextPlaceholder}
774
- className="h-8 w-full rounded-md border border-input bg-background px-3 text-sm outline-none focus:border-ring focus:ring-2 focus:ring-ring/30"
775
- />
776
- )}
777
788
  <Button
778
789
  type="button"
779
790
  size="sm"
780
791
  disabled={resolvedSubmitted || !valid}
781
792
  onClick={() => {
782
- onAnswer({ selections, text });
783
- setSubmitted(true);
793
+ onAnswer({
794
+ answers: questions.map((_, index) => ({
795
+ selections: answerAt(index).selections,
796
+ text: answerAt(index).text.trim(),
797
+ })),
798
+ });
784
799
  }}
785
800
  >
786
801
  {resolvedSubmitted ? "Submitted" : "Submit"}
787
802
  </Button>
788
- {externalAnswer && (
789
- <p className="text-xs text-muted-foreground">
790
- 已回答:{externalAnswer}
791
- </p>
792
- )}
793
803
  </section>
794
804
  );
795
805
  }
@@ -800,7 +810,6 @@ function CamelRichPart({
800
810
  onApprove,
801
811
  onSuggestion,
802
812
  onAnswer,
803
- replyText,
804
813
  getToolApproval,
805
814
  }: {
806
815
  part: unknown;
@@ -808,7 +817,6 @@ function CamelRichPart({
808
817
  onApprove: (approvalId: string, decision: ToolApprovalDecision) => void;
809
818
  onSuggestion: (text: string) => void;
810
819
  onAnswer: (payload: unknown) => void;
811
- replyText?: string;
812
820
  getToolApproval?: (part: unknown) => ToolApprovalView | undefined;
813
821
  }) {
814
822
  const kind = normalizedPartType(part);
@@ -824,7 +832,6 @@ function CamelRichPart({
824
832
  return (
825
833
  <CamelAskUser
826
834
  part={part}
827
- submittedAnswer={replyText}
828
835
  onAnswer={onAnswer}
829
836
  />
830
837
  );
@@ -924,7 +931,6 @@ function AssistantPart({
924
931
  onApprove,
925
932
  onSuggestion,
926
933
  onAnswer,
927
- replyText,
928
934
  getToolApproval,
929
935
  resolveUrl,
930
936
  }: {
@@ -933,7 +939,6 @@ function AssistantPart({
933
939
  onApprove: (approvalId: string, decision: ToolApprovalDecision) => void;
934
940
  onSuggestion: (text: string) => void;
935
941
  onAnswer: (payload: unknown) => void;
936
- replyText?: string;
937
942
  getToolApproval?: (part: unknown) => ToolApprovalView | undefined;
938
943
  resolveUrl?: MessageUrlResolver;
939
944
  }) {
@@ -960,7 +965,6 @@ function AssistantPart({
960
965
  onApprove={onApprove}
961
966
  onSuggestion={onSuggestion}
962
967
  onAnswer={onAnswer}
963
- replyText={replyText}
964
968
  getToolApproval={getToolApproval}
965
969
  />
966
970
  );
@@ -1137,7 +1141,6 @@ function RenderAssistantPart({
1137
1141
  onApprove,
1138
1142
  onSuggestion,
1139
1143
  onAnswer,
1140
- replyText,
1141
1144
  getToolApproval,
1142
1145
  resolveUrl,
1143
1146
  }: {
@@ -1146,7 +1149,6 @@ function RenderAssistantPart({
1146
1149
  onApprove: (approvalId: string, decision: ToolApprovalDecision) => void;
1147
1150
  onSuggestion: (text: string) => void;
1148
1151
  onAnswer: (payload: unknown) => void;
1149
- replyText?: string;
1150
1152
  getToolApproval?: (part: unknown) => ToolApprovalView | undefined;
1151
1153
  resolveUrl?: MessageUrlResolver;
1152
1154
  }) {
@@ -1160,7 +1162,6 @@ function RenderAssistantPart({
1160
1162
  onApprove={onApprove}
1161
1163
  onSuggestion={onSuggestion}
1162
1164
  onAnswer={onAnswer}
1163
- replyText={replyText}
1164
1165
  getToolApproval={getToolApproval}
1165
1166
  resolveUrl={resolveUrl}
1166
1167
  />
@@ -1200,7 +1201,6 @@ function AssistantTurn({
1200
1201
  onApprove={onApprove}
1201
1202
  onSuggestion={onSuggestion}
1202
1203
  onAnswer={onAnswer}
1203
- replyText={turn.replyText}
1204
1204
  getToolApproval={getToolApproval}
1205
1205
  resolveUrl={resolveUrl}
1206
1206
  />
@@ -77,6 +77,12 @@ function stringField(record: UnknownRecord, ...keys: string[]): string {
77
77
  return "";
78
78
  }
79
79
 
80
+ function stringArray(value: unknown): string[] {
81
+ return Array.isArray(value)
82
+ ? value.filter((item): item is string => typeof item === "string")
83
+ : [];
84
+ }
85
+
80
86
  function numberField(record: UnknownRecord, ...keys: string[]): number | undefined {
81
87
  for (const key of keys) {
82
88
  const value = record[key];
@@ -342,17 +348,7 @@ function fileSummary(
342
348
  }
343
349
 
344
350
  function questionsOf(input: UnknownRecord): UnknownRecord[] {
345
- if (Array.isArray(input.questions)) return input.questions.map(recordOf);
346
- if (typeof input.question === "string") {
347
- return [
348
- {
349
- question: input.question,
350
- header: input.header,
351
- options: input.options,
352
- },
353
- ];
354
- }
355
- return [];
351
+ return Array.isArray(input.questions) ? input.questions.map(recordOf) : [];
356
352
  }
357
353
 
358
354
  function summaryFor(
@@ -387,7 +383,7 @@ function summaryFor(
387
383
  complete: "Edited",
388
384
  });
389
385
  case "bash": {
390
- const command = stringField(input, "command", "cmd");
386
+ const command = stringField(input, "script", "command", "cmd");
391
387
  const description = stringField(input, "description");
392
388
  const label = description || truncate(command || "command", 30);
393
389
  return running
@@ -822,7 +818,7 @@ function EditDetails({ presentation }: CamelToolDetailsProps) {
822
818
 
823
819
  function BashDetails({ presentation }: CamelToolDetailsProps) {
824
820
  const { input, output, outputText } = presentation;
825
- const command = stringField(input, "command", "cmd");
821
+ const command = stringField(input, "script", "command", "cmd");
826
822
  const outputRecord = recordOf(output);
827
823
  const parsedFromText = (() => {
828
824
  if (!outputText.trim().startsWith("{")) return {};
@@ -1113,7 +1109,10 @@ function SkillDetails({
1113
1109
 
1114
1110
  function AskUserDetails({ presentation }: CamelToolDetailsProps) {
1115
1111
  const questions = questionsOf(presentation.input);
1116
- const answer = presentation.outputText;
1112
+ const output = recordOf(presentation.output);
1113
+ const answers = Array.isArray(output.answers)
1114
+ ? output.answers.map(recordOf)
1115
+ : [];
1117
1116
  return (
1118
1117
  <div className="space-y-2">
1119
1118
  {questions.length > 0 ? (
@@ -1123,7 +1122,12 @@ function AskUserDetails({ presentation }: CamelToolDetailsProps) {
1123
1122
  {stringField(question, "question", "prompt") || "Question"}
1124
1123
  </p>
1125
1124
  <p className="text-muted-foreground/60">
1126
- {answer || "—"}
1125
+ {[
1126
+ ...stringArray(answers[index]?.selections),
1127
+ ...(typeof answers[index]?.text === "string" && answers[index].text
1128
+ ? [answers[index].text]
1129
+ : []),
1130
+ ].join(" / ") || "—"}
1127
1131
  </p>
1128
1132
  </div>
1129
1133
  ))
@@ -41,7 +41,6 @@ export interface CamelAssistantTurn {
41
41
  };
42
42
  stepCount: number;
43
43
  copyText: string;
44
- replyText?: string;
45
44
  durationMs?: number;
46
45
  completedAt?: number;
47
46
  }
@@ -115,10 +114,6 @@ function standaloneKind(message: UIMessage): CamelStandaloneKind | null {
115
114
  return message.role === "user" ? "user" : null;
116
115
  }
117
116
 
118
- function isDirectUserMessage(message: UIMessage): boolean {
119
- return message.role === "user" && standaloneKind(message) === "user";
120
- }
121
-
122
117
  export function camelMessageText(message: UIMessage): string {
123
118
  return message.parts
124
119
  .filter(isVisibleText)
@@ -228,7 +223,6 @@ function turnDuration(message: UIMessage): number | undefined {
228
223
  export function projectCamelAssistantTurn(
229
224
  message: UIMessage,
230
225
  finalToolNames: readonly string[] = [],
231
- replyText?: string,
232
226
  ): CamelAssistantTurn {
233
227
  const parts = message.parts.map((part, partIndex) => ({
234
228
  messageId: message.id,
@@ -294,7 +288,6 @@ export function projectCamelAssistantTurn(
294
288
  parts.filter(({ part }) => part.type === "step-start").length,
295
289
  ),
296
290
  copyText: camelMessageText(message),
297
- replyText,
298
291
  durationMs: turnDuration(message),
299
292
  completedAt: camelCompletedAt(message),
300
293
  };
@@ -309,16 +302,7 @@ export function projectCamelMessages(
309
302
  if (message.role === "system") return;
310
303
  const displayKind = standaloneKind(message);
311
304
  if (message.role === "assistant" && displayKind === null) {
312
- const reply = messages
313
- .slice(index + 1)
314
- .find(isDirectUserMessage);
315
- items.push(
316
- projectCamelAssistantTurn(
317
- message,
318
- finalToolNames,
319
- reply ? camelMessageText(reply).trim() || undefined : undefined,
320
- ),
321
- );
305
+ items.push(projectCamelAssistantTurn(message, finalToolNames));
322
306
  return;
323
307
  }
324
308
  if (displayKind !== null) {
@@ -59,7 +59,7 @@ export const AttachmentPreview = forwardRef<
59
59
  attachment.status === "uploading"
60
60
  ? "上传中…"
61
61
  : attachment.status === "error"
62
- ? "上传失败"
62
+ ? "Upload failed"
63
63
  : [attachment.note, formatSize(attachment.size)].filter(Boolean).join(" · ");
64
64
  return (
65
65
  <div
@@ -211,10 +211,10 @@ function ComposerInner<TAttachment>(
211
211
  );
212
212
  const invalid = selected.filter((file) => !valid.includes(file));
213
213
  if (all.length > selected.length) {
214
- onValidationError?.(`最多只能添加 ${maxFiles ?? 1} 个附件`, all);
214
+ onValidationError?.(`You can add up to ${maxFiles ?? 1} attachments`, all);
215
215
  }
216
216
  if (invalid.length) {
217
- onValidationError?.("存在不支持或超过大小限制的文件", invalid);
217
+ onValidationError?.("Some files are unsupported or exceed the size limit", invalid);
218
218
  }
219
219
  if (valid.length) void onFilesAdded?.(valid);
220
220
  },
package/src/contracts.ts CHANGED
@@ -47,8 +47,6 @@ export interface PartRendererContext {
47
47
  activityId: string;
48
48
  isActive: boolean;
49
49
  isLatestOfKind: boolean;
50
- /** First user message after the assistant message that owns this part. */
51
- replyText?: string;
52
50
  approval?: ToolApprovalView;
53
51
  status: MessagePanelStatus;
54
52
  actions: MessagePanelActions;
@@ -93,7 +93,6 @@ interface MessageRowProps<T> {
93
93
  slots: MessagePanelSlots;
94
94
  status: MessagePanelProps<T>["status"];
95
95
  latestActivityByKind: ReadonlyMap<string, string>;
96
- replyText?: string;
97
96
  }
98
97
 
99
98
  function MessageRow<T>({
@@ -107,7 +106,6 @@ function MessageRow<T>({
107
106
  slots,
108
107
  status,
109
108
  latestActivityByKind,
110
- replyText,
111
109
  }: MessageRowProps<T>) {
112
110
  const role = roleOf(adapter, message);
113
111
  const parts = adapter.getParts(message);
@@ -186,7 +184,6 @@ function MessageRow<T>({
186
184
  isLatestOfKind:
187
185
  key === undefined ||
188
186
  latestActivityByKind.get(key) === activityId,
189
- replyText,
190
187
  approval: adapter.getToolApproval?.(part),
191
188
  status,
192
189
  actions,
@@ -255,25 +252,6 @@ function MessagePanelInner<T>(
255
252
  });
256
253
  return latest;
257
254
  }, [adapter, ids, messages]);
258
- const replyTextByMessageId = useMemo(() => {
259
- const replies = new Map<string, string>();
260
- messages.forEach((message, messageIndex) => {
261
- if (roleOf(adapter, message) !== "assistant") return;
262
- for (
263
- let replyIndex = messageIndex + 1;
264
- replyIndex < messages.length;
265
- replyIndex += 1
266
- ) {
267
- const reply = messages[replyIndex];
268
- if (roleOf(adapter, reply) !== "user") continue;
269
- const text = messageText(adapter, reply).trim();
270
- if (text) replies.set(ids[messageIndex], text);
271
- break;
272
- }
273
- });
274
- return replies;
275
- }, [adapter, ids, messages]);
276
-
277
255
  return (
278
256
  <div
279
257
  ref={ref}
@@ -299,7 +277,7 @@ function MessagePanelInner<T>(
299
277
  ? slots.error(error)
300
278
  : slots.error ?? (
301
279
  <PanelStatus kind="error">
302
- {error instanceof Error ? error.message : "这一轮出错了,请重试。"}
280
+ {error instanceof Error ? error.message : "This turn failed. Please try again."}
303
281
  </PanelStatus>
304
282
  ))}
305
283
  <Conversation>
@@ -332,7 +310,6 @@ function MessagePanelInner<T>(
332
310
  slots={slots}
333
311
  status={status}
334
312
  latestActivityByKind={latestActivityByKind}
335
- replyText={replyTextByMessageId.get(ids[index])}
336
313
  />
337
314
  ))}
338
315
  {showPending && (