@yushaw/sanqian-chat 0.2.25 → 0.2.30

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.
@@ -72,7 +72,8 @@ interface ChatUiConfigSerializable {
72
72
  /** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
73
73
  fontSize?: ChatFontSize;
74
74
  accentColor?: string;
75
- locale?: Locale;
75
+ /** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
76
+ locale?: Locale | string;
76
77
  strings?: Partial<ChatUiStrings>;
77
78
  alwaysOnTop?: boolean;
78
79
  }
@@ -72,7 +72,8 @@ interface ChatUiConfigSerializable {
72
72
  /** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
73
73
  fontSize?: ChatFontSize;
74
74
  accentColor?: string;
75
- locale?: Locale;
75
+ /** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
76
+ locale?: Locale | string;
76
77
  strings?: Partial<ChatUiStrings>;
77
78
  alwaysOnTop?: boolean;
78
79
  }
@@ -77,7 +77,8 @@ interface ChatUiConfigSerializable {
77
77
  /** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
78
78
  fontSize?: ChatFontSize;
79
79
  accentColor?: string;
80
- locale?: Locale;
80
+ /** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
81
+ locale?: Locale | string;
81
82
  strings?: Partial<ChatUiStrings>;
82
83
  alwaysOnTop?: boolean;
83
84
  }
@@ -1231,7 +1232,7 @@ interface HistoryModalProps {
1231
1232
  }
1232
1233
  declare const HistoryModal: react.NamedExoticComponent<HistoryModalProps>;
1233
1234
 
1234
- declare function resolveChatStrings(locale?: Locale, overrides?: Partial<ChatUiStrings>): ChatUiStrings;
1235
+ declare function resolveChatStrings(locale?: Locale | string, overrides?: Partial<ChatUiStrings>): ChatUiStrings;
1235
1236
 
1236
1237
  type AlertType = 'warning' | 'error';
1237
1238
  interface AlertAction {
@@ -77,7 +77,8 @@ interface ChatUiConfigSerializable {
77
77
  /** Font size scale: small (13px), normal (14px), large (16px), extra-large (18px) */
78
78
  fontSize?: ChatFontSize;
79
79
  accentColor?: string;
80
- locale?: Locale;
80
+ /** Supports Locale plus BCP-47 variants (e.g. zh-CN, en-US) */
81
+ locale?: Locale | string;
81
82
  strings?: Partial<ChatUiStrings>;
82
83
  alwaysOnTop?: boolean;
83
84
  }
@@ -1231,7 +1232,7 @@ interface HistoryModalProps {
1231
1232
  }
1232
1233
  declare const HistoryModal: react.NamedExoticComponent<HistoryModalProps>;
1233
1234
 
1234
- declare function resolveChatStrings(locale?: Locale, overrides?: Partial<ChatUiStrings>): ChatUiStrings;
1235
+ declare function resolveChatStrings(locale?: Locale | string, overrides?: Partial<ChatUiStrings>): ChatUiStrings;
1235
1236
 
1236
1237
  type AlertType = 'warning' | 'error';
1237
1238
  interface AlertAction {
@@ -125,7 +125,6 @@ function useChat(options) {
125
125
  const pendingInterruptStreamIdRef = (0, import_react.useRef)(null);
126
126
  const currentAgentIdRef = (0, import_react.useRef)(null);
127
127
  const pendingCancelRef = (0, import_react.useRef)(false);
128
- const pendingCancelTimeoutRef = (0, import_react.useRef)(null);
129
128
  const pendingCancelFnRef = (0, import_react.useRef)(null);
130
129
  const suppressStreamRef = (0, import_react.useRef)(false);
131
130
  const currentBlocksRef = (0, import_react.useRef)([]);
@@ -152,7 +151,6 @@ function useChat(options) {
152
151
  isMountedRef.current = false;
153
152
  cancelRef.current?.();
154
153
  if (typewriterIntervalRef.current) clearTimeout(typewriterIntervalRef.current);
155
- if (pendingCancelTimeoutRef.current) clearTimeout(pendingCancelTimeoutRef.current);
156
154
  };
157
155
  }, []);
158
156
  (0, import_react.useEffect)(() => {
@@ -215,10 +213,6 @@ function useChat(options) {
215
213
  const clearPendingCancel = (0, import_react.useCallback)(() => {
216
214
  pendingCancelRef.current = false;
217
215
  pendingCancelFnRef.current = null;
218
- if (pendingCancelTimeoutRef.current) {
219
- clearTimeout(pendingCancelTimeoutRef.current);
220
- pendingCancelTimeoutRef.current = null;
221
- }
222
216
  }, []);
223
217
  const handleStreamEvent = (0, import_react.useCallback)((event, assistantMessageId) => {
224
218
  if (!isMountedRef.current) return;
@@ -723,9 +717,12 @@ function useChat(options) {
723
717
  );
724
718
  cancelRef.current = cancel;
725
719
  if (pendingCancelRef.current) {
726
- clearPendingCancel();
720
+ pendingCancelFnRef.current = cancel;
727
721
  cancel();
728
- cancelRef.current = null;
722
+ if (currentRunIdRef.current) {
723
+ clearPendingCancel();
724
+ cancelRef.current = null;
725
+ }
729
726
  }
730
727
  return true;
731
728
  } catch (err) {
@@ -751,23 +748,15 @@ function useChat(options) {
751
748
  await trySendMessage(content, sendOptions);
752
749
  }, [trySendMessage]);
753
750
  const stopStreaming = (0, import_react.useCallback)(() => {
754
- const shouldDelayCancel = !currentRunIdRef.current;
755
- if (shouldDelayCancel) {
756
- pendingCancelRef.current = true;
757
- pendingCancelFnRef.current = cancelRef.current;
758
- suppressStreamRef.current = true;
759
- if (pendingCancelTimeoutRef.current) {
760
- clearTimeout(pendingCancelTimeoutRef.current);
751
+ pendingCancelRef.current = true;
752
+ pendingCancelFnRef.current = cancelRef.current;
753
+ suppressStreamRef.current = true;
754
+ if (cancelRef.current) {
755
+ cancelRef.current();
756
+ if (currentRunIdRef.current) {
757
+ clearPendingCancel();
758
+ cancelRef.current = null;
761
759
  }
762
- pendingCancelTimeoutRef.current = setTimeout(() => {
763
- pendingCancelRef.current = false;
764
- pendingCancelFnRef.current = null;
765
- pendingCancelTimeoutRef.current = null;
766
- suppressStreamRef.current = false;
767
- }, 1500);
768
- } else {
769
- cancelRef.current?.();
770
- cancelRef.current = null;
771
760
  }
772
761
  flushTypewriter();
773
762
  setMessages((prev) => {
@@ -795,10 +784,6 @@ function useChat(options) {
795
784
  resetStreamBuffers();
796
785
  currentRunIdRef.current = null;
797
786
  pendingInterruptStreamIdRef.current = null;
798
- if (!shouldDelayCancel) {
799
- clearPendingCancel();
800
- suppressStreamRef.current = false;
801
- }
802
787
  setIsStreaming(false);
803
788
  setIsLoading(false);
804
789
  }, [clearPendingCancel, flushTypewriter, resetStreamBuffers]);
@@ -853,11 +838,13 @@ function useChat(options) {
853
838
  clearPendingCancel();
854
839
  }, [clearPendingCancel, resetStreamBuffers]);
855
840
  const sendHitlResponse = (0, import_react.useCallback)((response) => {
856
- adapter.sendHitlResponse?.(
857
- response,
858
- currentRunIdRef.current ?? void 0,
859
- pendingInterruptStreamIdRef.current ?? void 0
860
- );
841
+ const runId = currentRunIdRef.current ?? void 0;
842
+ const streamId = pendingInterruptStreamIdRef.current ?? void 0;
843
+ if (streamId) {
844
+ adapter.sendHitlResponse?.(response, runId, streamId);
845
+ return;
846
+ }
847
+ adapter.sendHitlResponse?.(response, runId);
861
848
  }, [adapter]);
862
849
  const approveHitl = (0, import_react.useCallback)((remember = false) => {
863
850
  sendHitlResponse({ approved: true, remember });
@@ -1041,7 +1028,7 @@ var CHAT_UI_STRINGS = {
1041
1028
  hitlSubmit: "Submit",
1042
1029
  hitlCancel: "Cancel",
1043
1030
  hitlRememberChoice: "Remember this choice",
1044
- hitlRequiredField: "This field is required",
1031
+ hitlRequiredField: "Please enter a response before submitting",
1045
1032
  hitlTimeoutIn: "Timeout in",
1046
1033
  hitlSeconds: "s",
1047
1034
  hitlExecuteTool: "Execute",
@@ -1100,7 +1087,7 @@ var CHAT_UI_STRINGS = {
1100
1087
  hitlSubmit: "\u63D0\u4EA4",
1101
1088
  hitlCancel: "\u53D6\u6D88",
1102
1089
  hitlRememberChoice: "\u8BB0\u4F4F\u672C\u6B21\u9009\u62E9",
1103
- hitlRequiredField: "\u6B64\u9879\u5FC5\u586B",
1090
+ hitlRequiredField: "\u8BF7\u8F93\u5165\u56DE\u590D\u540E\u518D\u63D0\u4EA4",
1104
1091
  hitlTimeoutIn: "\u8D85\u65F6\u5269\u4F59",
1105
1092
  hitlSeconds: "\u79D2",
1106
1093
  hitlExecuteTool: "\u6267\u884C",
@@ -1123,9 +1110,15 @@ var CHAT_UI_STRINGS = {
1123
1110
  remove: "\u79FB\u9664"
1124
1111
  }
1125
1112
  };
1113
+ function normalizeLocale(locale) {
1114
+ const normalized = (locale || "en").toLowerCase();
1115
+ if (normalized.startsWith("zh")) return "zh";
1116
+ return "en";
1117
+ }
1126
1118
  function resolveChatStrings(locale = "en", overrides) {
1119
+ const normalizedLocale = normalizeLocale(locale);
1127
1120
  return {
1128
- ...CHAT_UI_STRINGS[locale],
1121
+ ...CHAT_UI_STRINGS[normalizedLocale],
1129
1122
  ...overrides
1130
1123
  };
1131
1124
  }
@@ -8039,7 +8032,7 @@ var defaultStrings = {
8039
8032
  submit: "Submit",
8040
8033
  cancel: "Cancel",
8041
8034
  rememberChoice: "Remember this choice",
8042
- requiredField: "This field is required",
8035
+ requiredField: "Please enter a response before submitting",
8043
8036
  timeoutIn: "Timeout in",
8044
8037
  seconds: "s",
8045
8038
  executeTool: "Execute",
@@ -8050,23 +8043,6 @@ var defaultStrings = {
8050
8043
  approvalRequest: "Approval Request",
8051
8044
  inputRequest: "Input Request"
8052
8045
  };
8053
- var defaultZhStrings = {
8054
- approve: "\u6279\u51C6",
8055
- reject: "\u62D2\u7EDD",
8056
- submit: "\u63D0\u4EA4",
8057
- cancel: "\u53D6\u6D88",
8058
- rememberChoice: "\u8BB0\u4F4F\u672C\u6B21\u9009\u62E9",
8059
- requiredField: "\u6B64\u9879\u5FC5\u586B",
8060
- timeoutIn: "\u8D85\u65F6\u5269\u4F59",
8061
- seconds: "\u79D2",
8062
- executeTool: "\u6267\u884C",
8063
- toolLabel: "\u5DE5\u5177",
8064
- argsLabel: "\u53C2\u6570",
8065
- defaultPrefix: "\u9ED8\u8BA4",
8066
- enterResponse: "\u8BF7\u8F93\u5165\u4F60\u7684\u56DE\u590D...",
8067
- approvalRequest: "\u9700\u8981\u5BA1\u6279",
8068
- inputRequest: "\u9700\u8981\u8F93\u5165"
8069
- };
8070
8046
  var riskColors = {
8071
8047
  low: {
8072
8048
  bg: "bg-blue-500/10",
@@ -8096,42 +8072,15 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8096
8072
  isDarkMode = false,
8097
8073
  strings = {}
8098
8074
  }) {
8099
- const mergedStrings = { ...defaultStrings, ...strings };
8075
+ const t = { ...defaultStrings, ...strings };
8100
8076
  const isApproval = interrupt.type === "approval_request";
8101
8077
  const isUserInput = interrupt.type === "user_input_request";
8102
- const cjkRegex = /[\u3400-\u9fff]/;
8103
- const hasCjkContent = cjkRegex.test(
8104
- [
8105
- interrupt.question || "",
8106
- interrupt.context || "",
8107
- interrupt.reason || "",
8108
- interrupt.tool || "",
8109
- ...interrupt.options || []
8110
- ].join(" ")
8111
- );
8112
- const t = hasCjkContent ? {
8113
- ...mergedStrings,
8114
- approve: mergedStrings.approve === defaultStrings.approve ? defaultZhStrings.approve : mergedStrings.approve,
8115
- reject: mergedStrings.reject === defaultStrings.reject ? defaultZhStrings.reject : mergedStrings.reject,
8116
- submit: mergedStrings.submit === defaultStrings.submit ? defaultZhStrings.submit : mergedStrings.submit,
8117
- cancel: mergedStrings.cancel === defaultStrings.cancel ? defaultZhStrings.cancel : mergedStrings.cancel,
8118
- rememberChoice: mergedStrings.rememberChoice === defaultStrings.rememberChoice ? defaultZhStrings.rememberChoice : mergedStrings.rememberChoice,
8119
- requiredField: mergedStrings.requiredField === defaultStrings.requiredField ? defaultZhStrings.requiredField : mergedStrings.requiredField,
8120
- timeoutIn: mergedStrings.timeoutIn === defaultStrings.timeoutIn ? defaultZhStrings.timeoutIn : mergedStrings.timeoutIn,
8121
- seconds: mergedStrings.seconds === defaultStrings.seconds ? defaultZhStrings.seconds : mergedStrings.seconds,
8122
- executeTool: mergedStrings.executeTool === defaultStrings.executeTool ? defaultZhStrings.executeTool : mergedStrings.executeTool,
8123
- toolLabel: mergedStrings.toolLabel === defaultStrings.toolLabel ? defaultZhStrings.toolLabel : mergedStrings.toolLabel,
8124
- argsLabel: mergedStrings.argsLabel === defaultStrings.argsLabel ? defaultZhStrings.argsLabel : mergedStrings.argsLabel,
8125
- defaultPrefix: mergedStrings.defaultPrefix === defaultStrings.defaultPrefix ? defaultZhStrings.defaultPrefix : mergedStrings.defaultPrefix,
8126
- enterResponse: mergedStrings.enterResponse === defaultStrings.enterResponse ? defaultZhStrings.enterResponse : mergedStrings.enterResponse,
8127
- approvalRequest: mergedStrings.approvalRequest === defaultStrings.approvalRequest ? defaultZhStrings.approvalRequest : mergedStrings.approvalRequest,
8128
- inputRequest: mergedStrings.inputRequest === defaultStrings.inputRequest ? defaultZhStrings.inputRequest : mergedStrings.inputRequest
8129
- } : mergedStrings;
8130
8078
  const [answer, setAnswer] = (0, import_react24.useState)(interrupt.default || "");
8131
8079
  const [selectedIndices, setSelectedIndices] = (0, import_react24.useState)([]);
8132
8080
  const [isComposing, setIsComposing] = (0, import_react24.useState)(false);
8133
8081
  const [timeLeft, setTimeLeft] = (0, import_react24.useState)(interrupt.timeout ?? null);
8134
8082
  const [rememberChoice, setRememberChoice] = (0, import_react24.useState)(false);
8083
+ const [hasAttemptedSubmit, setHasAttemptedSubmit] = (0, import_react24.useState)(false);
8135
8084
  const inputRef = (0, import_react24.useRef)(null);
8136
8085
  const textareaRef = (0, import_react24.useRef)(null);
8137
8086
  const riskLevel = interrupt.risk_level || "medium";
@@ -8162,18 +8111,37 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8162
8111
  }, 1e3);
8163
8112
  return () => clearInterval(timer);
8164
8113
  }, [timeLeft, isUserInput, onSubmit, onCancel]);
8114
+ const hasOptions = Boolean(interrupt.options && interrupt.options.length > 0);
8115
+ const isRequiredMissing = isUserInput && !!interrupt.required && (hasOptions ? selectedIndices.length === 0 : !answer.trim());
8116
+ const showRequiredError = hasAttemptedSubmit && isRequiredMissing;
8165
8117
  const handleOptionToggle = (index) => {
8118
+ if (hasAttemptedSubmit) {
8119
+ setHasAttemptedSubmit(false);
8120
+ }
8166
8121
  if (interrupt.multi_select) {
8167
8122
  setSelectedIndices((prev) => prev.includes(index) ? prev.filter((i) => i !== index) : [...prev, index]);
8168
8123
  } else {
8169
8124
  setSelectedIndices([index]);
8170
8125
  }
8171
8126
  };
8127
+ const handleAnswerChange = (value) => {
8128
+ if (hasAttemptedSubmit) {
8129
+ setHasAttemptedSubmit(false);
8130
+ }
8131
+ setAnswer(value);
8132
+ };
8172
8133
  const handleSubmit = () => {
8173
8134
  if (isApproval) {
8174
8135
  onApprove?.(rememberChoice);
8175
8136
  } else if (isUserInput) {
8176
- const hasOptions = interrupt.options && interrupt.options.length > 0;
8137
+ if (isRequiredMissing) {
8138
+ setHasAttemptedSubmit(true);
8139
+ if (!hasOptions) {
8140
+ inputRef.current?.focus();
8141
+ textareaRef.current?.focus();
8142
+ }
8143
+ return;
8144
+ }
8177
8145
  if (hasOptions) {
8178
8146
  const selectedAnswers = selectedIndices.map((i) => interrupt.options[i]).join(", ");
8179
8147
  onSubmit?.({
@@ -8181,11 +8149,6 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8181
8149
  selected_indices: selectedIndices
8182
8150
  });
8183
8151
  } else {
8184
- if (interrupt.required && !answer.trim()) {
8185
- inputRef.current?.focus();
8186
- textareaRef.current?.focus();
8187
- return;
8188
- }
8189
8152
  onSubmit?.({ answer: answer || interrupt.default || "" });
8190
8153
  }
8191
8154
  }
@@ -8229,13 +8192,10 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8229
8192
  lineHeight: 1.55,
8230
8193
  letterSpacing: "0.01em"
8231
8194
  };
8232
- const questionIconStyle = {
8233
- background: isDarkMode ? "rgba(59, 130, 246, 0.26)" : "rgba(59, 130, 246, 0.16)",
8234
- color: isDarkMode ? "#dbeafe" : "#1d4ed8"
8235
- };
8195
+ const showHeaderRow = isApproval || timeLeft !== null && timeLeft > 0;
8236
8196
  const cancelButtonClass = isDarkMode ? "flex-1 rounded-lg border border-zinc-500 bg-zinc-700/80 px-3 py-2 text-sm font-semibold text-zinc-100 transition-colors hover:bg-zinc-600" : "flex-1 rounded-lg border border-zinc-300 bg-zinc-100 px-3 py-2 text-sm font-semibold text-zinc-900 transition-colors hover:bg-zinc-200";
8237
- const isSubmitDisabled = isUserInput && interrupt.required && !answer.trim() && (!interrupt.options || selectedIndices.length === 0);
8238
- const submitButtonClass = `flex-1 rounded-lg border px-3 py-2 text-sm font-semibold transition-colors ${isSubmitDisabled ? "cursor-not-allowed" : "hover:brightness-105"}`;
8197
+ const isSubmitBlocked = isRequiredMissing;
8198
+ const submitButtonClass = `flex-1 rounded-lg border px-3 py-2 text-sm font-semibold transition-colors ${isSubmitBlocked ? "cursor-pointer" : "hover:brightness-105"}`;
8239
8199
  const cancelButtonStyle = isDarkMode ? {
8240
8200
  background: "rgba(63, 63, 70, 0.82)",
8241
8201
  color: "#f4f4f5",
@@ -8245,7 +8205,7 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8245
8205
  color: "#18181b",
8246
8206
  borderColor: "rgba(113, 113, 122, 0.45)"
8247
8207
  };
8248
- const submitButtonStyle = isSubmitDisabled ? isDarkMode ? {
8208
+ const submitButtonStyle = isSubmitBlocked ? isDarkMode ? {
8249
8209
  background: "rgba(82, 82, 91, 0.72)",
8250
8210
  color: "#d4d4d8",
8251
8211
  borderColor: "rgba(161, 161, 170, 0.35)",
@@ -8266,6 +8226,14 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8266
8226
  borderColor: "transparent",
8267
8227
  boxShadow: "0 2px 10px rgba(37, 99, 235, 0.3)"
8268
8228
  };
8229
+ const inputValidationStyle = showRequiredError ? isDarkMode ? {
8230
+ borderColor: "rgba(248, 113, 113, 0.62)",
8231
+ boxShadow: "0 0 0 2px rgba(248, 113, 113, 0.2)"
8232
+ } : {
8233
+ borderColor: "rgba(220, 38, 38, 0.45)",
8234
+ boxShadow: "0 0 0 2px rgba(220, 38, 38, 0.11)"
8235
+ } : void 0;
8236
+ const requiredHintStyle = isDarkMode ? { color: "rgba(252, 165, 165, 0.92)" } : { color: "rgba(185, 28, 28, 0.84)" };
8269
8237
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8270
8238
  "div",
8271
8239
  {
@@ -8275,8 +8243,8 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8275
8243
  "aria-modal": "true",
8276
8244
  "aria-label": isApproval ? t.approvalRequest : t.inputRequest,
8277
8245
  children: [
8278
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "mb-2 flex items-start justify-between gap-2", children: [
8279
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex min-w-0 items-center gap-2", children: isApproval ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8246
+ showHeaderRow && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: `mb-2 flex items-start gap-2 ${isApproval ? "justify-between" : "justify-end"}`, children: [
8247
+ isApproval && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "flex min-w-0 items-center gap-2", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8280
8248
  "span",
8281
8249
  {
8282
8250
  className: `inline-flex items-center gap-1 rounded-full px-2 py-0.5 text-xs font-medium ${riskStyle.bg} ${riskStyle.border} ${riskStyle.text} border`,
@@ -8285,7 +8253,7 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8285
8253
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "capitalize", children: riskLevel })
8286
8254
  ]
8287
8255
  }
8288
- ) : /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "inline-flex h-7 w-7 items-center justify-center rounded-full text-base font-semibold", style: questionIconStyle, children: "?" }) }),
8256
+ ) }),
8289
8257
  timeLeft !== null && timeLeft > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: `text-xs ${textSecondary} whitespace-nowrap`, children: [
8290
8258
  t.timeoutIn,
8291
8259
  " ",
@@ -8314,31 +8282,45 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8314
8282
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("code", { className: "text-[10px]", children: JSON.stringify(interrupt.args, null, 0) })
8315
8283
  ] })
8316
8284
  ] }),
8317
- isUserInput && interrupt.options && interrupt.options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "space-y-1.5", children: interrupt.options.map((option, index) => {
8318
- const isSelected = selectedIndices.includes(index);
8319
- const optionStyle = isSelected ? isDarkMode ? { borderColor: "rgba(96, 165, 250, 0.75)", background: "rgba(59, 130, 246, 0.24)" } : { borderColor: "rgba(37, 99, 235, 0.36)", background: "rgba(239, 246, 255, 0.98)" } : isDarkMode ? { borderColor: "rgba(113, 113, 122, 0.6)", background: "rgba(9, 9, 11, 0.84)" } : { borderColor: "rgba(113, 113, 122, 0.34)", background: "rgba(250, 250, 250, 0.96)" };
8320
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8321
- "label",
8285
+ isUserInput && interrupt.options && interrupt.options.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "space-y-1.5", children: [
8286
+ interrupt.options.map((option, index) => {
8287
+ const isSelected = selectedIndices.includes(index);
8288
+ const optionStyle = isSelected ? isDarkMode ? { borderColor: "rgba(96, 165, 250, 0.75)", background: "rgba(59, 130, 246, 0.24)" } : { borderColor: "rgba(37, 99, 235, 0.36)", background: "rgba(239, 246, 255, 0.98)" } : isDarkMode ? { borderColor: "rgba(113, 113, 122, 0.6)", background: "rgba(9, 9, 11, 0.84)" } : { borderColor: "rgba(113, 113, 122, 0.34)", background: "rgba(250, 250, 250, 0.96)" };
8289
+ return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8290
+ "label",
8291
+ {
8292
+ className: `flex cursor-pointer items-center gap-2 rounded p-2 transition-colors ${isSelected ? isDarkMode ? "border-blue-500/50 bg-blue-500/20" : "border-blue-200 bg-blue-50" : `${inputBg} border-transparent hover:border-zinc-300`} border`,
8293
+ style: optionStyle,
8294
+ children: [
8295
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
8296
+ "input",
8297
+ {
8298
+ type: interrupt.multi_select ? "checkbox" : "radio",
8299
+ name: "hitl-options",
8300
+ checked: isSelected,
8301
+ onChange: () => handleOptionToggle(index),
8302
+ style: optionControlStyle
8303
+ }
8304
+ ),
8305
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `text-sm ${textPrimary} break-words ${isSelected ? "font-semibold" : "font-medium"}`, children: option })
8306
+ ]
8307
+ },
8308
+ index
8309
+ );
8310
+ }),
8311
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "min-h-[18px]", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8312
+ "div",
8322
8313
  {
8323
- className: `flex cursor-pointer items-center gap-2 rounded p-2 transition-colors ${isSelected ? isDarkMode ? "border-blue-500/50 bg-blue-500/20" : "border-blue-200 bg-blue-50" : `${inputBg} border-transparent hover:border-zinc-300`} border`,
8324
- style: optionStyle,
8314
+ className: `flex items-center gap-1 text-xs transition-all duration-150 ${showRequiredError ? "translate-y-0 opacity-100" : "-translate-y-1 opacity-0"}`,
8315
+ style: requiredHintStyle,
8316
+ "aria-live": "polite",
8325
8317
  children: [
8326
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
8327
- "input",
8328
- {
8329
- type: interrupt.multi_select ? "checkbox" : "radio",
8330
- name: "hitl-options",
8331
- checked: isSelected,
8332
- onChange: () => handleOptionToggle(index),
8333
- style: optionControlStyle
8334
- }
8335
- ),
8336
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: `text-sm ${textPrimary} break-words ${isSelected ? "font-semibold" : "font-medium"}`, children: option })
8318
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "inline-block h-1.5 w-1.5 rounded-full bg-current opacity-75" }),
8319
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: t.requiredField })
8337
8320
  ]
8338
- },
8339
- index
8340
- );
8341
- }) }),
8321
+ }
8322
+ ) })
8323
+ ] }),
8342
8324
  isUserInput && (!interrupt.options || interrupt.options.length === 0) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
8343
8325
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
8344
8326
  "input",
@@ -8346,15 +8328,27 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8346
8328
  ref: inputRef,
8347
8329
  type: "text",
8348
8330
  value: answer,
8349
- onChange: (e) => setAnswer(e.target.value),
8331
+ onChange: (e) => handleAnswerChange(e.target.value),
8350
8332
  onKeyDown: handleKeyDown,
8351
8333
  onCompositionStart: () => setIsComposing(true),
8352
8334
  onCompositionEnd: () => setIsComposing(false),
8353
8335
  placeholder: interrupt.default ? `${t.defaultPrefix}: ${interrupt.default}` : t.enterResponse,
8354
- className: `w-full rounded-lg border px-3 py-2 text-sm ${inputBorder} ${inputBg} ${textPrimary} placeholder:${textSecondary} focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500/40`
8336
+ className: `w-full rounded-lg border px-3 py-2 text-sm ${inputBorder} ${inputBg} ${textPrimary} placeholder:${textSecondary} ${showRequiredError ? "focus:border-red-400 focus:ring-red-400/25" : "focus:border-blue-500 focus:ring-blue-500/40"} focus:outline-none focus:ring-2`,
8337
+ style: inputValidationStyle
8355
8338
  }
8356
8339
  ),
8357
- interrupt.required && !answer.trim() && /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("p", { className: "mt-1 text-xs text-red-500", children: t.requiredField })
8340
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("div", { className: "mt-1 min-h-[18px]", children: /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8341
+ "div",
8342
+ {
8343
+ className: `flex items-center gap-1 text-xs transition-all duration-150 ${showRequiredError ? "translate-y-0 opacity-100" : "-translate-y-1 opacity-0"}`,
8344
+ style: requiredHintStyle,
8345
+ "aria-live": "polite",
8346
+ children: [
8347
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "inline-block h-1.5 w-1.5 rounded-full bg-current opacity-75" }),
8348
+ /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { children: t.requiredField })
8349
+ ]
8350
+ }
8351
+ ) })
8358
8352
  ] }),
8359
8353
  isApproval && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8360
8354
  "label",
@@ -8390,7 +8384,7 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8390
8384
  "button",
8391
8385
  {
8392
8386
  onClick: handleSubmit,
8393
- disabled: isSubmitDisabled,
8387
+ "aria-disabled": isSubmitBlocked,
8394
8388
  className: submitButtonClass,
8395
8389
  style: submitButtonStyle,
8396
8390
  children: isApproval ? t.approve : t.submit
@@ -9221,9 +9215,10 @@ var SanqianChat = (0, import_react29.memo)(function SanqianChat2({
9221
9215
  const { themeClass, isDarkMode } = useResolvedTheme(resolvedConfig?.theme ?? "auto");
9222
9216
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
9223
9217
  const resolvedLogo = logo ?? resolvedConfig?.logo;
9218
+ const resolvedLocale = (0, import_react29.useMemo)(() => normalizeLocale(resolvedConfig?.locale), [resolvedConfig?.locale]);
9224
9219
  const strings = (0, import_react29.useMemo)(
9225
- () => resolveChatStrings(resolvedConfig?.locale, resolvedConfig?.strings),
9226
- [resolvedConfig?.locale, resolvedConfig?.strings]
9220
+ () => resolveChatStrings(resolvedLocale, resolvedConfig?.strings),
9221
+ [resolvedLocale, resolvedConfig?.strings]
9227
9222
  );
9228
9223
  const headerConfig = (0, import_react29.useMemo)(
9229
9224
  () => {
@@ -9409,7 +9404,7 @@ var SanqianChat = (0, import_react29.memo)(function SanqianChat2({
9409
9404
  isResourceAttached: resourcePicker.isResourceAttached,
9410
9405
  pickerError: resourcePicker.error,
9411
9406
  disabled: !!chat.pendingInterrupt || chat.isLoading,
9412
- locale: resolvedConfig?.locale
9407
+ locale: resolvedLocale
9413
9408
  }
9414
9409
  ) : void 0
9415
9410
  }
@@ -9808,7 +9803,10 @@ var FloatingChat = (0, import_react35.memo)(function FloatingChat2({
9808
9803
  minWidth: "100vw"
9809
9804
  }), [accentStyle]);
9810
9805
  const resolvedLogo = logo ?? resolvedConfig?.logo;
9811
- const resolvedLocale = resolvedConfig?.locale ?? locale;
9806
+ const resolvedLocale = (0, import_react35.useMemo)(
9807
+ () => normalizeLocale(resolvedConfig?.locale ?? locale),
9808
+ [resolvedConfig?.locale, locale]
9809
+ );
9812
9810
  const resolvedStrings = (0, import_react35.useMemo)(
9813
9811
  () => resolveChatStrings(resolvedLocale, resolvedConfig?.strings),
9814
9812
  [resolvedLocale, resolvedConfig?.strings]
@@ -10452,9 +10450,10 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10452
10450
  const { themeClass, isDarkMode: resolvedIsDarkMode } = useResolvedTheme(themeMode);
10453
10451
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
10454
10452
  const resolvedLogo = logo ?? resolvedConfig?.logo;
10453
+ const resolvedLocale = (0, import_react38.useMemo)(() => normalizeLocale(resolvedConfig?.locale), [resolvedConfig?.locale]);
10455
10454
  const baseStrings = (0, import_react38.useMemo)(
10456
- () => resolveChatStrings(resolvedConfig?.locale, resolvedConfig?.strings),
10457
- [resolvedConfig?.locale, resolvedConfig?.strings]
10455
+ () => resolveChatStrings(resolvedLocale, resolvedConfig?.strings),
10456
+ [resolvedLocale, resolvedConfig?.strings]
10458
10457
  );
10459
10458
  const mergedStrings = (0, import_react38.useMemo)(() => ({ ...baseStrings, ...strings }), [baseStrings, strings]);
10460
10459
  const headerConfig = (0, import_react38.useMemo)(
@@ -10784,7 +10783,7 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10784
10783
  ] }) }),
10785
10784
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex-1" }),
10786
10785
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-0.5", style: { WebkitAppRegion: "no-drag" }, children: [
10787
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PanelHeaderButtons, { locale: resolvedConfig?.locale }),
10786
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PanelHeaderButtons, { locale: resolvedLocale }),
10788
10787
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "chat-tooltip-wrapper", children: [
10789
10788
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10790
10789
  "button",
@@ -10910,7 +10909,7 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10910
10909
  isResourceAttached: resourcePicker.isResourceAttached,
10911
10910
  pickerError: resourcePicker.error,
10912
10911
  disabled: disableInput,
10913
- locale: resolvedConfig?.locale === "zh" ? "zh" : "en",
10912
+ locale: resolvedLocale,
10914
10913
  themeClass
10915
10914
  }
10916
10915
  ) : null;