@blade-hq/agent-kit 0.5.31 → 0.5.33

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 (33) hide show
  1. package/dist/{SkillStatusBar-CvgcEiSi.d.ts → SkillStatusBar-Dp93pqlP.d.ts} +14 -3
  2. package/dist/{blade-client-DoodJJDz.d.ts → blade-client-DS7lV19a.d.ts} +4 -1
  3. package/dist/{chunk-ZWO6YBLA.js → chunk-DQXXSHCR.js} +5 -3
  4. package/dist/chunk-DQXXSHCR.js.map +1 -0
  5. package/dist/{chunk-OPMRX65X.js → chunk-EBTZAB6X.js} +13 -5
  6. package/dist/chunk-EBTZAB6X.js.map +1 -0
  7. package/dist/{chunk-BUQRNLEE.js → chunk-MGXPILAM.js} +3 -3
  8. package/dist/{chunk-KMQQDKGU.js → chunk-PMG4N3JO.js} +19 -3
  9. package/dist/chunk-PMG4N3JO.js.map +1 -0
  10. package/dist/{chunk-HFVB57MR.js → chunk-SGOR4YXZ.js} +63 -18
  11. package/dist/chunk-SGOR4YXZ.js.map +1 -0
  12. package/dist/{chunk-4XBNTPVS.js → chunk-UR6C4AJB.js} +2 -2
  13. package/dist/client/index.d.ts +145 -1
  14. package/dist/client/index.js +1 -1
  15. package/dist/react/api/vibe-coding.d.ts +2 -2
  16. package/dist/react/api/vibe-coding.js +2 -2
  17. package/dist/react/components/chat/index.d.ts +3 -3
  18. package/dist/react/components/chat/index.js +5 -5
  19. package/dist/react/components/plan/index.js +4 -4
  20. package/dist/react/components/session/index.js +3 -3
  21. package/dist/react/components/workspace/index.d.ts +2 -2
  22. package/dist/react/components/workspace/index.js +3 -3
  23. package/dist/react/index.d.ts +10 -6
  24. package/dist/react/index.js +8 -6
  25. package/dist/react/index.js.map +1 -1
  26. package/dist/{sessions-DFGsAOPa.d.ts → sessions-DaddUdO6.d.ts} +4 -1
  27. package/package.json +1 -1
  28. package/dist/chunk-HFVB57MR.js.map +0 -1
  29. package/dist/chunk-KMQQDKGU.js.map +0 -1
  30. package/dist/chunk-OPMRX65X.js.map +0 -1
  31. package/dist/chunk-ZWO6YBLA.js.map +0 -1
  32. /package/dist/{chunk-BUQRNLEE.js.map → chunk-MGXPILAM.js.map} +0 -0
  33. /package/dist/{chunk-4XBNTPVS.js.map → chunk-UR6C4AJB.js.map} +0 -0
@@ -5,12 +5,12 @@ import {
5
5
  import {
6
6
  getSessionFilePath,
7
7
  resolveSessionFilePreviewTarget
8
- } from "./chunk-KMQQDKGU.js";
8
+ } from "./chunk-PMG4N3JO.js";
9
9
  import {
10
10
  formatToolName,
11
11
  getAuthedUrl,
12
12
  useUiStore
13
- } from "./chunk-OPMRX65X.js";
13
+ } from "./chunk-EBTZAB6X.js";
14
14
  import {
15
15
  cn,
16
16
  copyToClipboard
@@ -1933,4 +1933,4 @@ export {
1933
1933
  PlanSummaryCard,
1934
1934
  extractLatestPlanMessages
1935
1935
  };
1936
- //# sourceMappingURL=chunk-BUQRNLEE.js.map
1936
+ //# sourceMappingURL=chunk-MGXPILAM.js.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  apiFetchResponse,
3
3
  getAuthedUrl
4
- } from "./chunk-OPMRX65X.js";
4
+ } from "./chunk-EBTZAB6X.js";
5
5
 
6
6
  // src/react/lib/session-file-preview.ts
7
7
  var IMAGE_EXTS = /* @__PURE__ */ new Set(["png", "jpg", "jpeg", "gif", "svg", "webp", "ico", "bmp"]);
@@ -11,6 +11,7 @@ var PPT_EXTS = /* @__PURE__ */ new Set(["pptx", "ppt"]);
11
11
  var CSV_EXTS = /* @__PURE__ */ new Set(["csv"]);
12
12
  var MARKDOWN_EXTS = /* @__PURE__ */ new Set(["md", "markdown"]);
13
13
  var HTML_EXTS = /* @__PURE__ */ new Set(["html", "htm"]);
14
+ var OFFICE_PDF_EXTS = /* @__PURE__ */ new Set(["vsdx", "vsd", "odt", "ods", "odp", "odg", "rtf", "wps", "wpd"]);
14
15
  function getExt(fileName) {
15
16
  return fileName.split(".").pop()?.toLowerCase() ?? "";
16
17
  }
@@ -20,6 +21,9 @@ function getDisplayFileName(filePath, fileName) {
20
21
  function getSessionFilePath(sessionId, filePath) {
21
22
  return `/api/sessions/${sessionId}/files/${encodeURIComponent(filePath)}`;
22
23
  }
24
+ function getSessionPreviewPdfPath(sessionId, filePath) {
25
+ return `/api/sessions/${sessionId}/preview-pdf/${encodeURIComponent(filePath)}`;
26
+ }
23
27
  function getSessionFileDownloadUrl(sessionId, filePath) {
24
28
  return getAuthedUrl(getSessionFilePath(sessionId, filePath));
25
29
  }
@@ -55,7 +59,18 @@ function buildBinaryPreviewTarget(fileUrl, downloadUrl, filePath, fileName) {
55
59
  function buildSessionBinaryPreviewTarget(sessionId, filePath, fileName) {
56
60
  const fileUrl = getSessionFilePath(sessionId, filePath);
57
61
  const downloadUrl = getSessionFileDownloadUrl(sessionId, filePath);
58
- return buildBinaryPreviewTarget(fileUrl, downloadUrl, filePath, fileName);
62
+ const common = buildBinaryPreviewTarget(fileUrl, downloadUrl, filePath, fileName);
63
+ if (common) return common;
64
+ const resolvedFileName = getDisplayFileName(filePath, fileName);
65
+ const ext = getExt(resolvedFileName);
66
+ if (OFFICE_PDF_EXTS.has(ext)) {
67
+ const pdfUrl = getAuthedUrl(getSessionPreviewPdfPath(sessionId, filePath));
68
+ return { type: "pdf", content: pdfUrl, title: resolvedFileName, key: filePath };
69
+ }
70
+ return null;
71
+ }
72
+ function isOfficePdfPreviewable(fileName) {
73
+ return OFFICE_PDF_EXTS.has(getExt(fileName));
59
74
  }
60
75
  function resolveTextPreviewType(fileName) {
61
76
  const ext = getExt(fileName);
@@ -98,7 +113,8 @@ export {
98
113
  getSessionFilePath,
99
114
  buildBinaryPreviewTarget,
100
115
  buildSessionBinaryPreviewTarget,
116
+ isOfficePdfPreviewable,
101
117
  resolveTextPreviewType,
102
118
  resolveSessionFilePreviewTarget
103
119
  };
104
- //# sourceMappingURL=chunk-KMQQDKGU.js.map
120
+ //# sourceMappingURL=chunk-PMG4N3JO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/react/lib/session-file-preview.ts"],"sourcesContent":["import { apiFetchResponse, getAuthedUrl } from \"../api/client\"\nimport type { PreviewTarget } from \"../stores/ui-store\"\n\nconst IMAGE_EXTS = new Set([\"png\", \"jpg\", \"jpeg\", \"gif\", \"svg\", \"webp\", \"ico\", \"bmp\"])\nconst DOCX_EXTS = new Set([\"docx\", \"doc\"])\nconst EXCEL_EXTS = new Set([\"xlsx\", \"xls\", \"xlsm\", \"xlsb\"])\nconst PPT_EXTS = new Set([\"pptx\", \"ppt\"])\nconst CSV_EXTS = new Set([\"csv\"])\nconst MARKDOWN_EXTS = new Set([\"md\", \"markdown\"])\nconst HTML_EXTS = new Set([\"html\", \"htm\"])\n\n/** 需要后端 soffice 转 PDF 才能预览的 Office 格式 */\nconst OFFICE_PDF_EXTS = new Set([\"vsdx\", \"vsd\", \"odt\", \"ods\", \"odp\", \"odg\", \"rtf\", \"wps\", \"wpd\"])\n\nfunction getExt(fileName: string): string {\n return fileName.split(\".\").pop()?.toLowerCase() ?? \"\"\n}\n\nfunction getDisplayFileName(filePath: string, fileName?: string): string {\n return fileName?.trim() || filePath.split(\"/\").pop() || filePath\n}\n\nexport function getSessionFilePath(sessionId: string, filePath: string): string {\n return `/api/sessions/${sessionId}/files/${encodeURIComponent(filePath)}`\n}\n\nfunction getSessionPreviewPdfPath(sessionId: string, filePath: string): string {\n return `/api/sessions/${sessionId}/preview-pdf/${encodeURIComponent(filePath)}`\n}\n\nfunction getSessionFileDownloadUrl(sessionId: string, filePath: string): string {\n return getAuthedUrl(getSessionFilePath(sessionId, filePath))\n}\n\n/**\n * 后端把可解码为 UTF-8 的文件返回为 text/plain,二进制文件返回各自的 MIME。\n * 只有文本类内容才能安全地整体读进内存渲染——其余一律不能读 body。\n */\nfunction isTextualContentType(contentType: string): boolean {\n return (\n contentType.startsWith(\"text/\") ||\n contentType.includes(\"json\") ||\n contentType.includes(\"xml\") ||\n contentType.includes(\"yaml\") ||\n contentType.includes(\"javascript\")\n )\n}\n\nexport function buildBinaryPreviewTarget(\n fileUrl: string,\n downloadUrl: string,\n filePath: string,\n fileName?: string,\n): PreviewTarget | null {\n const resolvedFileName = getDisplayFileName(filePath, fileName)\n const ext = getExt(resolvedFileName)\n\n if (IMAGE_EXTS.has(ext)) {\n return { type: \"image\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (ext === \"pdf\") {\n return { type: \"pdf\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (DOCX_EXTS.has(ext)) {\n return { type: \"docx\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (EXCEL_EXTS.has(ext)) {\n return { type: \"excel\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (PPT_EXTS.has(ext)) {\n return {\n type: \"ppt\",\n content: fileUrl,\n sourceUrl: downloadUrl,\n title: resolvedFileName,\n key: filePath,\n }\n }\n\n return null\n}\n\nexport function buildSessionBinaryPreviewTarget(\n sessionId: string,\n filePath: string,\n fileName?: string,\n): PreviewTarget | null {\n const fileUrl = getSessionFilePath(sessionId, filePath)\n const downloadUrl = getSessionFileDownloadUrl(sessionId, filePath)\n const common = buildBinaryPreviewTarget(fileUrl, downloadUrl, filePath, fileName)\n if (common) return common\n\n // 需要后端 soffice 转 PDF 的 Office 格式(Visio 等)\n const resolvedFileName = getDisplayFileName(filePath, fileName)\n const ext = getExt(resolvedFileName)\n if (OFFICE_PDF_EXTS.has(ext)) {\n const pdfUrl = getAuthedUrl(getSessionPreviewPdfPath(sessionId, filePath))\n return { type: \"pdf\", content: pdfUrl, title: resolvedFileName, key: filePath }\n }\n\n return null\n}\n\n/** 判断文件是否需要后端 soffice 转 PDF 才能预览 */\nexport function isOfficePdfPreviewable(fileName: string): boolean {\n return OFFICE_PDF_EXTS.has(getExt(fileName))\n}\n\nexport function resolveTextPreviewType(fileName: string): PreviewTarget[\"type\"] {\n const ext = getExt(fileName)\n if (MARKDOWN_EXTS.has(ext)) return \"markdown\"\n if (CSV_EXTS.has(ext)) return \"csv\"\n if (HTML_EXTS.has(ext)) return \"html\"\n return \"file\"\n}\n\nexport async function resolveSessionFilePreviewTarget(\n sessionId: string,\n filePath: string,\n fileName?: string,\n): Promise<PreviewTarget> {\n const resolvedFileName = getDisplayFileName(filePath, fileName)\n const binaryTarget = buildSessionBinaryPreviewTarget(sessionId, filePath, resolvedFileName)\n if (binaryTarget) {\n return binaryTarget\n }\n\n const downloadUrl = getSessionFileDownloadUrl(sessionId, filePath)\n\n // 其余文件按后端返回的 Content-Type 分派,而不是猜扩展名:\n // 视频/音频/压缩包等二进制文件绝不能读 body(整体读进内存会卡死页面),\n // 改为把下载 URL 交给原生 <video>/<audio> 流式播放,或回退为下载。\n const res = await apiFetchResponse(getSessionFilePath(sessionId, filePath))\n const contentType = (res.headers.get(\"content-type\") ?? \"\").toLowerCase()\n\n if (contentType.startsWith(\"video/\")) {\n void res.body?.cancel()\n return { type: \"video\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (contentType.startsWith(\"audio/\")) {\n void res.body?.cancel()\n return { type: \"audio\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n if (!isTextualContentType(contentType)) {\n void res.body?.cancel()\n return { type: \"download\", content: downloadUrl, title: resolvedFileName, key: filePath }\n }\n\n const content = await res.text()\n\n return {\n type: resolveTextPreviewType(resolvedFileName),\n content,\n title: resolvedFileName,\n key: filePath,\n }\n}\n"],"mappings":"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","names":[]}
@@ -8,7 +8,7 @@ import {
8
8
  getCodeLanguageFromFilename,
9
9
  parseAskUserQuestion,
10
10
  useHighlightedCodeHtml
11
- } from "./chunk-BUQRNLEE.js";
11
+ } from "./chunk-MGXPILAM.js";
12
12
  import {
13
13
  Collapsible,
14
14
  CollapsibleContent,
@@ -16,7 +16,7 @@ import {
16
16
  } from "./chunk-H62LH2AG.js";
17
17
  import {
18
18
  resolveSessionFilePreviewTarget
19
- } from "./chunk-KMQQDKGU.js";
19
+ } from "./chunk-PMG4N3JO.js";
20
20
  import {
21
21
  apiFetchResponse,
22
22
  buildMessageContent,
@@ -60,7 +60,7 @@ import {
60
60
  useUiBridgeStore,
61
61
  useUiStore,
62
62
  writeFile
63
- } from "./chunk-OPMRX65X.js";
63
+ } from "./chunk-EBTZAB6X.js";
64
64
  import {
65
65
  registerBridgeIframe,
66
66
  tapBridgeEvent
@@ -69,7 +69,7 @@ import {
69
69
  ModelOption,
70
70
  ModelsConfig,
71
71
  ModelsResource
72
- } from "./chunk-ZWO6YBLA.js";
72
+ } from "./chunk-DQXXSHCR.js";
73
73
  import {
74
74
  cn,
75
75
  copyToClipboard
@@ -198,6 +198,7 @@ import {
198
198
  Paperclip,
199
199
  Plus,
200
200
  Rocket,
201
+ RotateCcw,
201
202
  Scissors,
202
203
  Send,
203
204
  Sparkles as Sparkles2,
@@ -2046,6 +2047,7 @@ var SkillMention = Mention2.extend({
2046
2047
 
2047
2048
  // src/react/components/chat/ChatInput.tsx
2048
2049
  import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
2050
+ var DOUBLE_ESCAPE_MS = 450;
2049
2051
  function getEditorText(editor) {
2050
2052
  return editor?.getText({ blockSeparator: "\n" }) ?? "";
2051
2053
  }
@@ -2685,7 +2687,9 @@ function ChatInput({
2685
2687
  skillStatusBarInnerClassName,
2686
2688
  onResyncSkills,
2687
2689
  isResyncingSkills = false,
2688
- inputLabel = "\u8F93\u5165\u6D88\u606F"
2690
+ inputLabel = "\u8F93\u5165\u6D88\u606F",
2691
+ onDoubleEscape,
2692
+ composerHint
2689
2693
  }) {
2690
2694
  const resolvedInputLabel = inputLabel?.trim() || "\u8F93\u5165\u6D88\u606F";
2691
2695
  const [input, setInputInternal] = useState8(externalDraft?.value ?? "");
@@ -2734,6 +2738,7 @@ function ChatInput({
2734
2738
  const fileInputRef = useRef5(null);
2735
2739
  const folderInputRef = useRef5(null);
2736
2740
  const editorRef = useRef5(null);
2741
+ const lastEscapeAtRef = useRef5(0);
2737
2742
  const localImageUrlsRef = useRef5(/* @__PURE__ */ new Map());
2738
2743
  const activeSessionId = useSessionStore((state) => state.activeSessionId);
2739
2744
  const sessions = useSessionStore((state) => state.sessions);
@@ -2836,7 +2841,8 @@ function ChatInput({
2836
2841
  navigateUp: inputHistory.navigateUp,
2837
2842
  navigateDown: inputHistory.navigateDown,
2838
2843
  isNavigatingHistory: inputHistory.isNavigating,
2839
- handleSubmit: stableHandleSubmit
2844
+ handleSubmit: stableHandleSubmit,
2845
+ onDoubleEscape
2840
2846
  }));
2841
2847
  const syncEditorState = (editorInstance) => {
2842
2848
  const value = getEditorText(editorInstance);
@@ -3066,6 +3072,18 @@ function ChatInput({
3066
3072
  replaceEditorText(editorRef.current, nextEntry);
3067
3073
  return true;
3068
3074
  }
3075
+ if (state.onDoubleEscape && event.key === "Escape" && !event.isComposing && !event.repeat) {
3076
+ const now = Date.now();
3077
+ if (now - lastEscapeAtRef.current <= DOUBLE_ESCAPE_MS) {
3078
+ lastEscapeAtRef.current = 0;
3079
+ event.preventDefault();
3080
+ const rect = editorRef.current?.view.dom.getBoundingClientRect() ?? null;
3081
+ state.onDoubleEscape(rect);
3082
+ return true;
3083
+ }
3084
+ lastEscapeAtRef.current = now;
3085
+ return false;
3086
+ }
3069
3087
  if (event.key === "Enter" && !event.shiftKey && !event.isComposing) {
3070
3088
  event.preventDefault();
3071
3089
  if (state.connectionStatus !== "connected") {
@@ -3560,6 +3578,23 @@ function ChatInput({
3560
3578
  ]
3561
3579
  }
3562
3580
  ),
3581
+ onDoubleEscape ? /* @__PURE__ */ jsxs8(
3582
+ "button",
3583
+ {
3584
+ type: "button",
3585
+ onClick: () => {
3586
+ actionMenuRef.current?.removeAttribute("open");
3587
+ onDoubleEscape(
3588
+ editorRef.current?.view.dom.getBoundingClientRect() ?? null
3589
+ );
3590
+ },
3591
+ className: "flex w-full items-center gap-2 px-3 py-2 text-left transition-colors hover:bg-[hsl(var(--accent))]",
3592
+ children: [
3593
+ /* @__PURE__ */ jsx9(RotateCcw, { size: 13 }),
3594
+ "\u56DE\u5230\u4E4B\u524D\u7684\u67D0\u4E00\u6B65"
3595
+ ]
3596
+ }
3597
+ ) : null,
3563
3598
  onCommand ? /* @__PURE__ */ jsxs8(Fragment2, { children: [
3564
3599
  /* @__PURE__ */ jsxs8(
3565
3600
  "button",
@@ -3699,7 +3734,8 @@ function ChatInput({
3699
3734
  /* @__PURE__ */ jsxs8("div", { className: "space-y-0.5", children: [
3700
3735
  /* @__PURE__ */ jsx9("div", { children: "Shift+Enter \u6362\u884C" }),
3701
3736
  /* @__PURE__ */ jsx9("div", { children: "/ \u8C03\u7528\u6280\u80FD" }),
3702
- /* @__PURE__ */ jsx9("div", { children: "@ \u5F15\u7528\u6587\u4EF6" })
3737
+ /* @__PURE__ */ jsx9("div", { children: "@ \u5F15\u7528\u6587\u4EF6" }),
3738
+ composerHint && /* @__PURE__ */ jsx9("div", { children: composerHint })
3703
3739
  ] })
3704
3740
  ] }) })
3705
3741
  ] }),
@@ -4525,7 +4561,7 @@ ReasoningTrigger.displayName = "ReasoningTrigger";
4525
4561
  ReasoningContent.displayName = "ReasoningContent";
4526
4562
 
4527
4563
  // src/react/components/chat/AgentLoopBlock.tsx
4528
- import { Bot, Check as Check3, ChevronRight as ChevronRight4, FileText as FileText6, Loader2 as Loader24, MessageSquareMore as MessageSquareMore2 } from "lucide-react";
4564
+ import { Bot, Check as Check3, ChevronRight as ChevronRight4, FileText as FileText6, Loader2 as Loader25, MessageSquareMore as MessageSquareMore2 } from "lucide-react";
4529
4565
  import { useEffect as useEffect14, useMemo as useMemo14, useState as useState17 } from "react";
4530
4566
 
4531
4567
  // src/react/components/chat/ResourceIframe.tsx
@@ -4988,7 +5024,7 @@ function FileEditRenderer({ toolCall }) {
4988
5024
  import { useState as useState13 } from "react";
4989
5025
 
4990
5026
  // src/react/components/chat/ImageLightbox.tsx
4991
- import { ChevronLeft, ChevronRight as ChevronRight2, Download as Download2, ExternalLink, Minus, Plus as Plus2, RotateCcw, X as X3 } from "lucide-react";
5027
+ import { ChevronLeft, ChevronRight as ChevronRight2, Download as Download2, ExternalLink, Minus, Plus as Plus2, RotateCcw as RotateCcw2, X as X3 } from "lucide-react";
4992
5028
  import { useCallback as useCallback10, useEffect as useEffect11, useRef as useRef10, useState as useState12 } from "react";
4993
5029
  import { createPortal as createPortal3 } from "react-dom";
4994
5030
  import { Fragment as Fragment4, jsx as jsx17, jsxs as jsxs13 } from "react/jsx-runtime";
@@ -5219,7 +5255,7 @@ function ImageLightbox({ open, onOpenChange, images, initialIndex = 0 }) {
5219
5255
  type: "button",
5220
5256
  onClick: reset,
5221
5257
  className: "rounded-full p-1.5 text-white/80 transition-colors hover:bg-white/10 hover:text-white",
5222
- children: /* @__PURE__ */ jsx17(RotateCcw, { size: 16 })
5258
+ children: /* @__PURE__ */ jsx17(RotateCcw2, { size: 16 })
5223
5259
  }
5224
5260
  ),
5225
5261
  showNav && /* @__PURE__ */ jsxs13(Fragment4, { children: [
@@ -5820,6 +5856,7 @@ function buildAskUserPayload(argumentsJson) {
5820
5856
 
5821
5857
  // src/react/components/chat/UserMessageBubble.tsx
5822
5858
  import { useQueries } from "@tanstack/react-query";
5859
+ import { Loader2 as Loader24 } from "lucide-react";
5823
5860
  import { useEffect as useEffect13, useRef as useRef11, useState as useState16 } from "react";
5824
5861
 
5825
5862
  // src/react/lib/preview-dispatch.ts
@@ -6298,6 +6335,7 @@ function buildSessionFileUrl(sessionId, filePath) {
6298
6335
  `/api/sessions/${encodeURIComponent(sessionId)}/files/${encodeURIComponent(filePath)}`
6299
6336
  );
6300
6337
  }
6338
+ var isSending = (message) => message.status === "streaming";
6301
6339
  function UserMessageBubble({ message, className }) {
6302
6340
  const activeSessionId = useSessionStore((state) => state.activeSessionId);
6303
6341
  const _textContent = getTextContent(message.content);
@@ -6474,13 +6512,16 @@ function UserMessageBubble({ message, className }) {
6474
6512
  children: cleanText
6475
6513
  }
6476
6514
  ) }),
6477
- trimmedClean && /* @__PURE__ */ jsx29(
6515
+ trimmedClean && (isSending(message) ? /* @__PURE__ */ jsxs24("div", { className: "flex items-center gap-1.5 text-xs text-[hsl(var(--muted-foreground))]", children: [
6516
+ /* @__PURE__ */ jsx29(Loader24, { size: 12, className: "animate-spin" }),
6517
+ /* @__PURE__ */ jsx29("span", { children: "\u53D1\u9001\u4E2D" })
6518
+ ] }) : /* @__PURE__ */ jsx29(
6478
6519
  MessageActions,
6479
6520
  {
6480
6521
  content: cleanText,
6481
6522
  className: "opacity-0 transition-opacity group-hover:opacity-100"
6482
6523
  }
6483
- )
6524
+ ))
6484
6525
  ] }) });
6485
6526
  }
6486
6527
  function ErrorMessageBlock({
@@ -6669,7 +6710,7 @@ function AgentLoopBlock({ toolCall, sessionId, reasoning }) {
6669
6710
  },
6670
6711
  childToolCall.id
6671
6712
  )) : /* @__PURE__ */ jsxs25("div", { className: "flex items-center gap-2 px-4 py-1 text-xs text-[hsl(var(--muted-foreground))]", children: [
6672
- /* @__PURE__ */ jsx30(Loader24, { size: 12, className: "shrink-0 animate-spin text-blue-500" }),
6713
+ /* @__PURE__ */ jsx30(Loader25, { size: 12, className: "shrink-0 animate-spin text-blue-500" }),
6673
6714
  /* @__PURE__ */ jsx30("span", { children: "\u6B63\u5728\u542F\u52A8..." })
6674
6715
  ] }) }) : status === "awaiting_answer" ? /* @__PURE__ */ jsx30("div", { className: "flex flex-col gap-1 py-2", children: /* @__PURE__ */ jsx30("div", { className: "px-4 py-1 text-xs text-[hsl(var(--muted-foreground))]", children: "\u8BF7\u5728\u4E0B\u65B9\u5B50\u667A\u80FD\u4F53\u5BF9\u8BDD\u4E2D\u5B8C\u6210\u786E\u8BA4" }) }) : null
6675
6716
  ] }) : /* @__PURE__ */ jsxs25(Fragment8, { children: [
@@ -6792,7 +6833,7 @@ function AgentLoopBlock({ toolCall, sessionId, reasoning }) {
6792
6833
  message.entry_id ?? `${message.timestamp ?? "error"}-${index}`
6793
6834
  ) : null
6794
6835
  ) }) : status === "running" ? /* @__PURE__ */ jsxs25("div", { className: "px-3 py-2 text-[11px] text-[hsl(var(--muted-foreground))]", children: [
6795
- /* @__PURE__ */ jsx30(Loader24, { size: 12, className: "mr-1.5 inline animate-spin" }),
6836
+ /* @__PURE__ */ jsx30(Loader25, { size: 12, className: "mr-1.5 inline animate-spin" }),
6796
6837
  "\u6B63\u5728\u542F\u52A8..."
6797
6838
  ] }) : toolCall.result ? /* @__PURE__ */ jsx30("pre", { className: "max-h-[400px] overflow-auto px-3 py-2 whitespace-pre-wrap text-[11px] text-[hsl(var(--muted-foreground))]", children: typeof toolCall.result === "string" ? toolCall.result : JSON.stringify(toolCall.result, null, 2) }) : null
6798
6839
  ] })
@@ -7493,7 +7534,7 @@ function MemoryRefsHint({ refs: rawRefs }) {
7493
7534
  }
7494
7535
 
7495
7536
  // src/react/components/chat/CompactionCard.tsx
7496
- import { ChevronDown as ChevronDown2, ChevronRight as ChevronRight6, Loader2 as Loader25, Square as Square3, XCircle } from "lucide-react";
7537
+ import { ChevronDown as ChevronDown2, ChevronRight as ChevronRight6, Loader2 as Loader26, Square as Square3, XCircle } from "lucide-react";
7497
7538
  import { useState as useState19 } from "react";
7498
7539
  import { jsx as jsx32, jsxs as jsxs27 } from "react/jsx-runtime";
7499
7540
  var PERCENT_FORMATTER = new Intl.NumberFormat("zh-CN", {
@@ -7570,7 +7611,7 @@ function CompactionCard({
7570
7611
  children: [
7571
7612
  canExpand ? expanded ? /* @__PURE__ */ jsx32(ChevronDown2, { size: 12, className: "shrink-0" }) : /* @__PURE__ */ jsx32(ChevronRight6, { size: 12, className: "shrink-0" }) : null,
7572
7613
  /* @__PURE__ */ jsx32("span", { children: source.status === "streaming" ? /* @__PURE__ */ jsxs27("span", { className: "inline-flex items-center gap-1", children: [
7573
- /* @__PURE__ */ jsx32(Loader25, { size: 12, className: "animate-spin" }),
7614
+ /* @__PURE__ */ jsx32(Loader26, { size: 12, className: "animate-spin" }),
7574
7615
  "\u6B63\u5728\u538B\u7F29\u4E0A\u4E0B\u6587"
7575
7616
  ] }) : source.status === "failed" ? /* @__PURE__ */ jsxs27("span", { className: "inline-flex items-center gap-1 text-rose-500/80", children: [
7576
7617
  /* @__PURE__ */ jsx32(XCircle, { size: 12 }),
@@ -8398,6 +8439,8 @@ function ChatView({
8398
8439
  onResyncSkills,
8399
8440
  isResyncingSkills = false,
8400
8441
  inputLabel,
8442
+ onDoubleEscape,
8443
+ composerHint,
8401
8444
  classNames,
8402
8445
  components,
8403
8446
  renderers
@@ -8477,7 +8520,9 @@ function ChatView({
8477
8520
  skillStatusBarInnerClassName: classNames?.skillStatusBarInner,
8478
8521
  onResyncSkills,
8479
8522
  isResyncingSkills,
8480
- inputLabel
8523
+ inputLabel,
8524
+ onDoubleEscape,
8525
+ composerHint
8481
8526
  }
8482
8527
  )
8483
8528
  ] });
@@ -8534,4 +8579,4 @@ use-stick-to-bottom/dist/StickToBottom.js:
8534
8579
  * Licensed under the MIT License. See License.txt in the project root for license information.
8535
8580
  *--------------------------------------------------------------------------------------------*)
8536
8581
  */
8537
- //# sourceMappingURL=chunk-HFVB57MR.js.map
8582
+ //# sourceMappingURL=chunk-SGOR4YXZ.js.map