@yushaw/sanqian-chat 0.2.27 → 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.
@@ -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]);
@@ -1043,7 +1028,7 @@ var CHAT_UI_STRINGS = {
1043
1028
  hitlSubmit: "Submit",
1044
1029
  hitlCancel: "Cancel",
1045
1030
  hitlRememberChoice: "Remember this choice",
1046
- hitlRequiredField: "This field is required",
1031
+ hitlRequiredField: "Please enter a response before submitting",
1047
1032
  hitlTimeoutIn: "Timeout in",
1048
1033
  hitlSeconds: "s",
1049
1034
  hitlExecuteTool: "Execute",
@@ -1102,7 +1087,7 @@ var CHAT_UI_STRINGS = {
1102
1087
  hitlSubmit: "\u63D0\u4EA4",
1103
1088
  hitlCancel: "\u53D6\u6D88",
1104
1089
  hitlRememberChoice: "\u8BB0\u4F4F\u672C\u6B21\u9009\u62E9",
1105
- hitlRequiredField: "\u6B64\u9879\u5FC5\u586B",
1090
+ hitlRequiredField: "\u8BF7\u8F93\u5165\u56DE\u590D\u540E\u518D\u63D0\u4EA4",
1106
1091
  hitlTimeoutIn: "\u8D85\u65F6\u5269\u4F59",
1107
1092
  hitlSeconds: "\u79D2",
1108
1093
  hitlExecuteTool: "\u6267\u884C",
@@ -1125,9 +1110,15 @@ var CHAT_UI_STRINGS = {
1125
1110
  remove: "\u79FB\u9664"
1126
1111
  }
1127
1112
  };
1113
+ function normalizeLocale(locale) {
1114
+ const normalized = (locale || "en").toLowerCase();
1115
+ if (normalized.startsWith("zh")) return "zh";
1116
+ return "en";
1117
+ }
1128
1118
  function resolveChatStrings(locale = "en", overrides) {
1119
+ const normalizedLocale = normalizeLocale(locale);
1129
1120
  return {
1130
- ...CHAT_UI_STRINGS[locale],
1121
+ ...CHAT_UI_STRINGS[normalizedLocale],
1131
1122
  ...overrides
1132
1123
  };
1133
1124
  }
@@ -8041,7 +8032,7 @@ var defaultStrings = {
8041
8032
  submit: "Submit",
8042
8033
  cancel: "Cancel",
8043
8034
  rememberChoice: "Remember this choice",
8044
- requiredField: "This field is required",
8035
+ requiredField: "Please enter a response before submitting",
8045
8036
  timeoutIn: "Timeout in",
8046
8037
  seconds: "s",
8047
8038
  executeTool: "Execute",
@@ -8052,23 +8043,6 @@ var defaultStrings = {
8052
8043
  approvalRequest: "Approval Request",
8053
8044
  inputRequest: "Input Request"
8054
8045
  };
8055
- var defaultZhStrings = {
8056
- approve: "\u6279\u51C6",
8057
- reject: "\u62D2\u7EDD",
8058
- submit: "\u63D0\u4EA4",
8059
- cancel: "\u53D6\u6D88",
8060
- rememberChoice: "\u8BB0\u4F4F\u672C\u6B21\u9009\u62E9",
8061
- requiredField: "\u6B64\u9879\u5FC5\u586B",
8062
- timeoutIn: "\u8D85\u65F6\u5269\u4F59",
8063
- seconds: "\u79D2",
8064
- executeTool: "\u6267\u884C",
8065
- toolLabel: "\u5DE5\u5177",
8066
- argsLabel: "\u53C2\u6570",
8067
- defaultPrefix: "\u9ED8\u8BA4",
8068
- enterResponse: "\u8BF7\u8F93\u5165\u4F60\u7684\u56DE\u590D...",
8069
- approvalRequest: "\u9700\u8981\u5BA1\u6279",
8070
- inputRequest: "\u9700\u8981\u8F93\u5165"
8071
- };
8072
8046
  var riskColors = {
8073
8047
  low: {
8074
8048
  bg: "bg-blue-500/10",
@@ -8098,42 +8072,15 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8098
8072
  isDarkMode = false,
8099
8073
  strings = {}
8100
8074
  }) {
8101
- const mergedStrings = { ...defaultStrings, ...strings };
8075
+ const t = { ...defaultStrings, ...strings };
8102
8076
  const isApproval = interrupt.type === "approval_request";
8103
8077
  const isUserInput = interrupt.type === "user_input_request";
8104
- const cjkRegex = /[\u3400-\u9fff]/;
8105
- const hasCjkContent = cjkRegex.test(
8106
- [
8107
- interrupt.question || "",
8108
- interrupt.context || "",
8109
- interrupt.reason || "",
8110
- interrupt.tool || "",
8111
- ...interrupt.options || []
8112
- ].join(" ")
8113
- );
8114
- const t = hasCjkContent ? {
8115
- ...mergedStrings,
8116
- approve: mergedStrings.approve === defaultStrings.approve ? defaultZhStrings.approve : mergedStrings.approve,
8117
- reject: mergedStrings.reject === defaultStrings.reject ? defaultZhStrings.reject : mergedStrings.reject,
8118
- submit: mergedStrings.submit === defaultStrings.submit ? defaultZhStrings.submit : mergedStrings.submit,
8119
- cancel: mergedStrings.cancel === defaultStrings.cancel ? defaultZhStrings.cancel : mergedStrings.cancel,
8120
- rememberChoice: mergedStrings.rememberChoice === defaultStrings.rememberChoice ? defaultZhStrings.rememberChoice : mergedStrings.rememberChoice,
8121
- requiredField: mergedStrings.requiredField === defaultStrings.requiredField ? defaultZhStrings.requiredField : mergedStrings.requiredField,
8122
- timeoutIn: mergedStrings.timeoutIn === defaultStrings.timeoutIn ? defaultZhStrings.timeoutIn : mergedStrings.timeoutIn,
8123
- seconds: mergedStrings.seconds === defaultStrings.seconds ? defaultZhStrings.seconds : mergedStrings.seconds,
8124
- executeTool: mergedStrings.executeTool === defaultStrings.executeTool ? defaultZhStrings.executeTool : mergedStrings.executeTool,
8125
- toolLabel: mergedStrings.toolLabel === defaultStrings.toolLabel ? defaultZhStrings.toolLabel : mergedStrings.toolLabel,
8126
- argsLabel: mergedStrings.argsLabel === defaultStrings.argsLabel ? defaultZhStrings.argsLabel : mergedStrings.argsLabel,
8127
- defaultPrefix: mergedStrings.defaultPrefix === defaultStrings.defaultPrefix ? defaultZhStrings.defaultPrefix : mergedStrings.defaultPrefix,
8128
- enterResponse: mergedStrings.enterResponse === defaultStrings.enterResponse ? defaultZhStrings.enterResponse : mergedStrings.enterResponse,
8129
- approvalRequest: mergedStrings.approvalRequest === defaultStrings.approvalRequest ? defaultZhStrings.approvalRequest : mergedStrings.approvalRequest,
8130
- inputRequest: mergedStrings.inputRequest === defaultStrings.inputRequest ? defaultZhStrings.inputRequest : mergedStrings.inputRequest
8131
- } : mergedStrings;
8132
8078
  const [answer, setAnswer] = (0, import_react24.useState)(interrupt.default || "");
8133
8079
  const [selectedIndices, setSelectedIndices] = (0, import_react24.useState)([]);
8134
8080
  const [isComposing, setIsComposing] = (0, import_react24.useState)(false);
8135
8081
  const [timeLeft, setTimeLeft] = (0, import_react24.useState)(interrupt.timeout ?? null);
8136
8082
  const [rememberChoice, setRememberChoice] = (0, import_react24.useState)(false);
8083
+ const [hasAttemptedSubmit, setHasAttemptedSubmit] = (0, import_react24.useState)(false);
8137
8084
  const inputRef = (0, import_react24.useRef)(null);
8138
8085
  const textareaRef = (0, import_react24.useRef)(null);
8139
8086
  const riskLevel = interrupt.risk_level || "medium";
@@ -8164,18 +8111,37 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8164
8111
  }, 1e3);
8165
8112
  return () => clearInterval(timer);
8166
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;
8167
8117
  const handleOptionToggle = (index) => {
8118
+ if (hasAttemptedSubmit) {
8119
+ setHasAttemptedSubmit(false);
8120
+ }
8168
8121
  if (interrupt.multi_select) {
8169
8122
  setSelectedIndices((prev) => prev.includes(index) ? prev.filter((i) => i !== index) : [...prev, index]);
8170
8123
  } else {
8171
8124
  setSelectedIndices([index]);
8172
8125
  }
8173
8126
  };
8127
+ const handleAnswerChange = (value) => {
8128
+ if (hasAttemptedSubmit) {
8129
+ setHasAttemptedSubmit(false);
8130
+ }
8131
+ setAnswer(value);
8132
+ };
8174
8133
  const handleSubmit = () => {
8175
8134
  if (isApproval) {
8176
8135
  onApprove?.(rememberChoice);
8177
8136
  } else if (isUserInput) {
8178
- 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
+ }
8179
8145
  if (hasOptions) {
8180
8146
  const selectedAnswers = selectedIndices.map((i) => interrupt.options[i]).join(", ");
8181
8147
  onSubmit?.({
@@ -8183,11 +8149,6 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8183
8149
  selected_indices: selectedIndices
8184
8150
  });
8185
8151
  } else {
8186
- if (interrupt.required && !answer.trim()) {
8187
- inputRef.current?.focus();
8188
- textareaRef.current?.focus();
8189
- return;
8190
- }
8191
8152
  onSubmit?.({ answer: answer || interrupt.default || "" });
8192
8153
  }
8193
8154
  }
@@ -8231,13 +8192,10 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8231
8192
  lineHeight: 1.55,
8232
8193
  letterSpacing: "0.01em"
8233
8194
  };
8234
- const questionIconStyle = {
8235
- background: isDarkMode ? "rgba(59, 130, 246, 0.26)" : "rgba(59, 130, 246, 0.16)",
8236
- color: isDarkMode ? "#dbeafe" : "#1d4ed8"
8237
- };
8195
+ const showHeaderRow = isApproval || timeLeft !== null && timeLeft > 0;
8238
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";
8239
- const isSubmitDisabled = isUserInput && interrupt.required && !answer.trim() && (!interrupt.options || selectedIndices.length === 0);
8240
- 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"}`;
8241
8199
  const cancelButtonStyle = isDarkMode ? {
8242
8200
  background: "rgba(63, 63, 70, 0.82)",
8243
8201
  color: "#f4f4f5",
@@ -8247,7 +8205,7 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8247
8205
  color: "#18181b",
8248
8206
  borderColor: "rgba(113, 113, 122, 0.45)"
8249
8207
  };
8250
- const submitButtonStyle = isSubmitDisabled ? isDarkMode ? {
8208
+ const submitButtonStyle = isSubmitBlocked ? isDarkMode ? {
8251
8209
  background: "rgba(82, 82, 91, 0.72)",
8252
8210
  color: "#d4d4d8",
8253
8211
  borderColor: "rgba(161, 161, 170, 0.35)",
@@ -8268,6 +8226,14 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8268
8226
  borderColor: "transparent",
8269
8227
  boxShadow: "0 2px 10px rgba(37, 99, 235, 0.3)"
8270
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)" };
8271
8237
  return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8272
8238
  "div",
8273
8239
  {
@@ -8277,8 +8243,8 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8277
8243
  "aria-modal": "true",
8278
8244
  "aria-label": isApproval ? t.approvalRequest : t.inputRequest,
8279
8245
  children: [
8280
- /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { className: "mb-2 flex items-start justify-between gap-2", children: [
8281
- /* @__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)(
8282
8248
  "span",
8283
8249
  {
8284
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`,
@@ -8287,7 +8253,7 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8287
8253
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("span", { className: "capitalize", children: riskLevel })
8288
8254
  ]
8289
8255
  }
8290
- ) : /* @__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
+ ) }),
8291
8257
  timeLeft !== null && timeLeft > 0 && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("span", { className: `text-xs ${textSecondary} whitespace-nowrap`, children: [
8292
8258
  t.timeoutIn,
8293
8259
  " ",
@@ -8316,31 +8282,45 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8316
8282
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)("code", { className: "text-[10px]", children: JSON.stringify(interrupt.args, null, 0) })
8317
8283
  ] })
8318
8284
  ] }),
8319
- 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) => {
8320
- const isSelected = selectedIndices.includes(index);
8321
- 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)" };
8322
- return /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8323
- "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",
8324
8313
  {
8325
- 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`,
8326
- 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",
8327
8317
  children: [
8328
- /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
8329
- "input",
8330
- {
8331
- type: interrupt.multi_select ? "checkbox" : "radio",
8332
- name: "hitl-options",
8333
- checked: isSelected,
8334
- onChange: () => handleOptionToggle(index),
8335
- style: optionControlStyle
8336
- }
8337
- ),
8338
- /* @__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 })
8339
8320
  ]
8340
- },
8341
- index
8342
- );
8343
- }) }),
8321
+ }
8322
+ ) })
8323
+ ] }),
8344
8324
  isUserInput && (!interrupt.options || interrupt.options.length === 0) && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)("div", { children: [
8345
8325
  /* @__PURE__ */ (0, import_jsx_runtime12.jsx)(
8346
8326
  "input",
@@ -8348,15 +8328,27 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8348
8328
  ref: inputRef,
8349
8329
  type: "text",
8350
8330
  value: answer,
8351
- onChange: (e) => setAnswer(e.target.value),
8331
+ onChange: (e) => handleAnswerChange(e.target.value),
8352
8332
  onKeyDown: handleKeyDown,
8353
8333
  onCompositionStart: () => setIsComposing(true),
8354
8334
  onCompositionEnd: () => setIsComposing(false),
8355
8335
  placeholder: interrupt.default ? `${t.defaultPrefix}: ${interrupt.default}` : t.enterResponse,
8356
- 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
8357
8338
  }
8358
8339
  ),
8359
- 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
+ ) })
8360
8352
  ] }),
8361
8353
  isApproval && /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(
8362
8354
  "label",
@@ -8392,7 +8384,7 @@ var HitlCard = (0, import_react24.memo)(function HitlCard2({
8392
8384
  "button",
8393
8385
  {
8394
8386
  onClick: handleSubmit,
8395
- disabled: isSubmitDisabled,
8387
+ "aria-disabled": isSubmitBlocked,
8396
8388
  className: submitButtonClass,
8397
8389
  style: submitButtonStyle,
8398
8390
  children: isApproval ? t.approve : t.submit
@@ -9223,9 +9215,10 @@ var SanqianChat = (0, import_react29.memo)(function SanqianChat2({
9223
9215
  const { themeClass, isDarkMode } = useResolvedTheme(resolvedConfig?.theme ?? "auto");
9224
9216
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
9225
9217
  const resolvedLogo = logo ?? resolvedConfig?.logo;
9218
+ const resolvedLocale = (0, import_react29.useMemo)(() => normalizeLocale(resolvedConfig?.locale), [resolvedConfig?.locale]);
9226
9219
  const strings = (0, import_react29.useMemo)(
9227
- () => resolveChatStrings(resolvedConfig?.locale, resolvedConfig?.strings),
9228
- [resolvedConfig?.locale, resolvedConfig?.strings]
9220
+ () => resolveChatStrings(resolvedLocale, resolvedConfig?.strings),
9221
+ [resolvedLocale, resolvedConfig?.strings]
9229
9222
  );
9230
9223
  const headerConfig = (0, import_react29.useMemo)(
9231
9224
  () => {
@@ -9411,7 +9404,7 @@ var SanqianChat = (0, import_react29.memo)(function SanqianChat2({
9411
9404
  isResourceAttached: resourcePicker.isResourceAttached,
9412
9405
  pickerError: resourcePicker.error,
9413
9406
  disabled: !!chat.pendingInterrupt || chat.isLoading,
9414
- locale: resolvedConfig?.locale
9407
+ locale: resolvedLocale
9415
9408
  }
9416
9409
  ) : void 0
9417
9410
  }
@@ -9810,7 +9803,10 @@ var FloatingChat = (0, import_react35.memo)(function FloatingChat2({
9810
9803
  minWidth: "100vw"
9811
9804
  }), [accentStyle]);
9812
9805
  const resolvedLogo = logo ?? resolvedConfig?.logo;
9813
- const resolvedLocale = resolvedConfig?.locale ?? locale;
9806
+ const resolvedLocale = (0, import_react35.useMemo)(
9807
+ () => normalizeLocale(resolvedConfig?.locale ?? locale),
9808
+ [resolvedConfig?.locale, locale]
9809
+ );
9814
9810
  const resolvedStrings = (0, import_react35.useMemo)(
9815
9811
  () => resolveChatStrings(resolvedLocale, resolvedConfig?.strings),
9816
9812
  [resolvedLocale, resolvedConfig?.strings]
@@ -10454,9 +10450,10 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10454
10450
  const { themeClass, isDarkMode: resolvedIsDarkMode } = useResolvedTheme(themeMode);
10455
10451
  const accentStyle = useAccentStyle(resolvedConfig?.accentColor);
10456
10452
  const resolvedLogo = logo ?? resolvedConfig?.logo;
10453
+ const resolvedLocale = (0, import_react38.useMemo)(() => normalizeLocale(resolvedConfig?.locale), [resolvedConfig?.locale]);
10457
10454
  const baseStrings = (0, import_react38.useMemo)(
10458
- () => resolveChatStrings(resolvedConfig?.locale, resolvedConfig?.strings),
10459
- [resolvedConfig?.locale, resolvedConfig?.strings]
10455
+ () => resolveChatStrings(resolvedLocale, resolvedConfig?.strings),
10456
+ [resolvedLocale, resolvedConfig?.strings]
10460
10457
  );
10461
10458
  const mergedStrings = (0, import_react38.useMemo)(() => ({ ...baseStrings, ...strings }), [baseStrings, strings]);
10462
10459
  const headerConfig = (0, import_react38.useMemo)(
@@ -10786,7 +10783,7 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10786
10783
  ] }) }),
10787
10784
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)("div", { className: "flex-1" }),
10788
10785
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "flex items-center gap-0.5", style: { WebkitAppRegion: "no-drag" }, children: [
10789
- /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PanelHeaderButtons, { locale: resolvedConfig?.locale }),
10786
+ /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(PanelHeaderButtons, { locale: resolvedLocale }),
10790
10787
  /* @__PURE__ */ (0, import_jsx_runtime26.jsxs)("div", { className: "chat-tooltip-wrapper", children: [
10791
10788
  /* @__PURE__ */ (0, import_jsx_runtime26.jsx)(
10792
10789
  "button",
@@ -10912,7 +10909,7 @@ var CompactChat = (0, import_react38.memo)(function CompactChat2({
10912
10909
  isResourceAttached: resourcePicker.isResourceAttached,
10913
10910
  pickerError: resourcePicker.error,
10914
10911
  disabled: disableInput,
10915
- locale: resolvedConfig?.locale === "zh" ? "zh" : "en",
10912
+ locale: resolvedLocale,
10916
10913
  themeClass
10917
10914
  }
10918
10915
  ) : null;