@blade-hq/agent-react 2610.0.0-beta.70 → 2610.0.0-beta.72

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 (30) hide show
  1. package/README.md +160 -5
  2. package/dist/{chunk-MAAMCOAU.js → chunk-IA6J2NTB.js} +4 -3
  3. package/dist/components/AgentLoopBlock.d.ts +2 -1
  4. package/dist/components/AskUserQuestionBlock.d.ts +3 -1
  5. package/dist/components/AssistantTurnBlock.d.ts +8 -4
  6. package/dist/components/ChatInput.d.ts +7 -1
  7. package/dist/components/ChatSurface.d.ts +105 -1
  8. package/dist/components/ChatView.d.ts +1 -1
  9. package/dist/components/MessageList.d.ts +13 -4
  10. package/dist/components/PlanUpdateBlock.d.ts +2 -1
  11. package/dist/components/PostChatFollowupBlock.d.ts +5 -1
  12. package/dist/components/ToolCallBlock.d.ts +6 -2
  13. package/dist/components/TurnNavRail.d.ts +23 -0
  14. package/dist/components/UserMessageBubble.d.ts +6 -2
  15. package/dist/embed/blade-chat-element.d.ts +4 -3
  16. package/dist/embed/entry.d.ts +2 -0
  17. package/dist/{highlighted-body-B3W2YXNL-IASCZDV2.js → highlighted-body-B3W2YXNL-YF4GVJGP.js} +2 -2
  18. package/dist/hooks/use-turn-navigation.d.ts +117 -0
  19. package/dist/index.d.ts +6 -1
  20. package/dist/index.js +1911 -1176
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/utils.d.ts +15 -2
  23. package/dist/{mermaid-3ZIDBTTL-BPVVWTVV.js → mermaid-3ZIDBTTL-QL4YGD6I.js} +2 -2
  24. package/dist/style.css +488 -494
  25. package/dist/style.full.css +499 -498
  26. package/package.json +2 -2
  27. package/public-api.md +329 -4
  28. /package/dist/{chunk-MAAMCOAU.js.map → chunk-IA6J2NTB.js.map} +0 -0
  29. /package/dist/{highlighted-body-B3W2YXNL-IASCZDV2.js.map → highlighted-body-B3W2YXNL-YF4GVJGP.js.map} +0 -0
  30. /package/dist/{mermaid-3ZIDBTTL-BPVVWTVV.js.map → mermaid-3ZIDBTTL-QL4YGD6I.js.map} +0 -0
package/README.md CHANGED
@@ -193,8 +193,9 @@ MCP App 卡片(`tool_ui` block)在默认消息流里自动渲染,无需配
193
193
  文件成果以文件名为文本的标准下载链接展示;Vue / 纯 HTML 使用的 `<blade-chat>` 与 `ChatView` 行为一致。
194
194
 
195
195
  - 必须引入一份样式,按宿主有没有 Tailwind 二选一:
196
- - **`style.full.css`**(默认选它):布局兜底 + 编译好的 Tailwind 产物,自包含,宿主没装 Tailwind 也是完整视觉。
197
- - **`style.css`**:只有 `.blade-chat-*` 布局兜底。宿主自己装了 Tailwind、且 `content` 配了扫描 `node_modules/@blade-hq/agent-react` 时用它,可以少一份重复 CSS。选错不会报错,只是工具块、思考块内部会退化成没有布局的裸 HTML
196
+ - **`style.full.css`**(默认选它):CSS 变量、容器查询、Markdown 排版等兜底 + 编译好的 Tailwind 产物,自包含,宿主没装 Tailwind 也是完整视觉。
197
+ - **`style.css`**:只有兜底那部分(Tailwind 类表达不了的规则)。宿主自己装了 Tailwind、且 `content` 配了扫描 `node_modules/@blade-hq/agent-react` 时用它,可以少一份重复 CSS。选错不会报错,只是布局、配色、工具块和思考块会退化成裸 HTML——组件的视觉本来就只定义一次,都在它的 Tailwind 类上。
198
+ - 两份都用同一套层序 `@layer properties, theme, base, components, utilities, blade-chat-overrides;`(在 `src/scoped-preflight.css` 里声明一次;`properties` 是 Tailwind 的 `--tw-*` 初始值层,必须排在最前)。兜底规则在 `components` 层;Tailwind 工具类在 `utilities` 层;SDK 里那几条**必须压过自己工具类**的规则(窄容器里放宽用户消息、移动端聚焦不缩放、Markdown 与代码块排版)在最后一层 `blade-chat-overrides`(名字带 SDK 前缀,避免宿主已有的同名层把全局层序定到别处)——放进 `components` 会被同元素的工具类盖掉,等于没写。**宿主写的普通 CSS 不分层,按层序赢过这里所有层,与加载顺序无关,也不需要 `!important`**:这是「接入方覆盖必胜」的实现方式,改 `src/style.css` 时不要把规则挪出这两层。
198
199
  - 两份都不含 Tailwind preflight 这类全局 reset,引入后不会影响宿主页面自己的排版;组件需要的那点重置限定在 `.blade-chat` 子树内(见 `src/scoped-preflight.css`)。Shadow DOM 版(`<blade-chat>`)不受此限,它注入的是带 preflight 的完整产物。
199
200
  - 颜色走 CSS 变量,可整体换肤;深色页面要在根元素上写 `data-theme="dark"`,否则拿到的是默认的浅色变量。
200
201
  - `renderers.toolCall` 返回 `null` 时回落到默认工具卡片。
@@ -357,15 +358,28 @@ SDK 的 ChatView / `<blade-chat>` 不内置模型选择器(默认用后端配
357
358
 
358
359
  1. **深浅主题**:`<ChatView theme="dark" />` / `<blade-chat theme="dark">` 切内置深色,不传或 `"light"` 为浅色。宿主已经在祖先元素上写了 `data-theme="dark"` 时不用传,`ChatView` 会跟着变。`<blade-chat>` 渲染在 Shadow DOM 内,页面上的 `data-theme` 对它无效,只认 `theme` 属性。
359
360
 
360
- 2. **CSS 变量换肤**(React 与 `<blade-chat>` 通用):所有颜色走 `--primary`、`--background`、`--muted` 等变量(完整清单见 style.css 开头)。页面直接覆盖即可穿透 Shadow DOM:
361
+ 2. **CSS 变量换肤**(React 与 `<blade-chat>` 通用):所有颜色走 `--primary`、`--background`、`--muted` 等变量(完整清单见下面的「CSS 变量清单」,默认值在 style.css 开头)。页面直接覆盖即可穿透 Shadow DOM:
361
362
 
362
363
  ```css
363
364
  blade-chat { --primary: 262 83% 58%; height: 640px; }
364
365
  ```
365
366
 
366
- 3. **classNames props**(React):`ChatView` 的 `classNames` 把自定义 class(含你自己构建的 Tailwind 工具类)挂到根节点、消息区、输入区等关键节点上,见 `ChatViewClassNames`。
367
+ 3. **classNames props**(React):`ChatView` 的 `classNames` 把自定义 class(含你自己构建的 Tailwind 工具类)挂到各个区块上,见 `ChatViewClassNames`。SDK 内部用 `cn`(clsx + tailwind-merge)拼接,你传的类在最后一个参数,所以 `classNames={{ chatInput: "p-0" }}` 会直接盖掉组件内部的 `py-3`,与 CSS 加载顺序无关:
367
368
 
368
- 4. **内嵌 `<style>`(纯 HTML / Vue)**:`<blade-chat>` 的直接 `<style>` 子元素会被注入 Shadow DOM、排在内置样式之后,可用 `.blade-chat-*` 类名做任意深度定制:
369
+ ```tsx
370
+ <ChatView
371
+ sessionId={id}
372
+ classNames={{
373
+ root: "h-[640px] rounded-2xl border",
374
+ userBubble: "bg-sky-100 text-sky-900",
375
+ assistantText: "text-[15px] leading-7",
376
+ chatInput: "px-2",
377
+ inputInner: "bg-white shadow-sm",
378
+ }}
379
+ />
380
+ ```
381
+
382
+ 4. **内嵌 `<style>` 与 `::part`(纯 HTML / Vue)**:`<blade-chat>` 的直接 `<style>` 子元素会被注入 Shadow DOM、排在内置样式之后,可用 `.blade-chat-*` 类名做任意深度定制;每个主要区块同时带一个与锚点同名的 `part`(`blade-chat-user-bubble` → `part="user-bubble"`),页面外层用 `::part()` 也能选到它:
369
383
 
370
384
  ```html
371
385
  <blade-chat base-url="...">
@@ -376,6 +390,131 @@ blade-chat { --primary: 262 83% 58%; height: 640px; }
376
390
  </blade-chat>
377
391
  ```
378
392
 
393
+ ```css
394
+ blade-chat::part(user-bubble) { background: #eef2ff; }
395
+ blade-chat::part(input-toolbar) { gap: 12px; }
396
+ ```
397
+
398
+ ### classNames 覆盖哪些区块
399
+
400
+ 区块与锚点一一对应(键名 = 锚点名去掉 `blade-chat-` 前缀):
401
+
402
+ | `classNames` 键 | 锚点 | 落点 |
403
+ | --- | --- | --- |
404
+ | `root` | `.blade-chat` | 聊天根容器 |
405
+ | `banner` | `.blade-chat-banner` | 连接状态条 |
406
+ | `errorBar` | `.blade-chat-error-bar` | 顶部出错条 |
407
+ | `messageList` | `.blade-chat-messages` | 消息区 |
408
+ | `messagesScroll` | `.blade-chat-messages-scroll` | 消息区滚动内容(内边距) |
409
+ | `messagesContent` | `.blade-chat-messages-content` | 消息内容列(居中、最大宽度) |
410
+ | `empty` | `.blade-chat-empty` | 空状态 |
411
+ | `scrollToBottom` | `.blade-chat-scroll-bottom` | 「滚动到底部」按钮 |
412
+ | `userMessage` | `.blade-chat-user-row` | 用户消息行 |
413
+ | `userBubble` | `.blade-chat-user-bubble` | 用户气泡 |
414
+ | `fileCard` | `.blade-chat-file-card` | 消息里的文件附件 |
415
+ | `errorMessage` | `.blade-chat-error-row` | 消息流里的出错块 |
416
+ | `assistantTurn` | `.blade-chat-assistant-turn` | 一轮助手回复 |
417
+ | `thinking` | `.blade-chat-thinking` | 思考过程 |
418
+ | `assistantText` | `.blade-chat-assistant-text` | 助手正文 |
419
+ | `memoryRefs` | `.blade-chat-memory-refs` | 记忆引用 |
420
+ | `toolRow` | `.blade-chat-tool-row` | 执行过程里的工具行(智能体模式下工具调用都走这条路径) |
421
+ | `agentLoop` | `.blade-chat-agent-loop` | 子智能体行 |
422
+ | `askCard` | `.blade-chat-ask-card` | 提问卡 |
423
+ | `askOption` | `.blade-chat-ask-option` | 提问卡的每个选项 |
424
+ | `planPanel` | `.blade-chat-plan` | 输入框上方的计划面板 |
425
+ | `queuePanel` | `.blade-chat-queue` | 会话队列面板 |
426
+ | `followup` | `.blade-chat-followup` | 本轮小结与跟进区 |
427
+ | `followupAction` | `.blade-chat-followup-action` | 跟进建议按钮 |
428
+ | `chatInput` | `.blade-chat-input` | 输入区外层 |
429
+ | `inputInner` | `.blade-chat-input-inner` | 输入框卡片(边框、圆角、底色) |
430
+ | `textarea` | `.blade-chat-textarea` | 输入框 |
431
+ | `inputToolbar` | `.blade-chat-input-toolbar` | 输入框右侧按钮组 |
432
+
433
+ 同一批锚点也是普通 CSS 的抓手:锚点只负责「可被选中」,视觉规则不在它身上,所以对你的锚点写 `.blade-chat-user-bubble { ... }` 不会被 SDK 的样式盖掉(SDK 的规则都在 `@layer components` / `@layer blade-chat-overrides`,Tailwind 工具类在 `@layer utilities`,你的样式不分层,按 CSS 层序本来就赢)。
434
+
435
+ ### 锚点清单
436
+
437
+ 每个可见区块的根元素都固定带一个 `blade-chat-*` 类,构成公开契约(`src/style.css` 顶部的登记块是唯一来源,构建会校验它和组件源码、三份 CSS 产物一致):
438
+
439
+ | 区块 | 锚点 |
440
+ | --- | --- |
441
+ | 聊天根容器 | `.blade-chat` |
442
+ | 登录引导 / 连接状态条 / 出错条 / 回放条 | `.blade-chat-login`、`.blade-chat-banner`、`.blade-chat-error-bar`、`.blade-chat-replay-bar` |
443
+ | 消息区 | `.blade-chat-messages`、`.blade-chat-messages-scroll`、`.blade-chat-messages-content`、`.blade-chat-empty`、`.blade-chat-scroll-bottom`、`.blade-chat-scroll-bottom-label` |
444
+ | 用户消息 | `.blade-chat-user-row`、`.blade-chat-user-col`、`.blade-chat-user-bubble`、`.blade-chat-file-card`(+ `-icon` / `-name`) |
445
+ | 助手回复 | `.blade-chat-assistant-turn`、`.blade-chat-assistant-text`、`.blade-chat-thinking`、`.blade-chat-memory-refs`、`.blade-chat-render-error` |
446
+ | 工具调用 | `.blade-chat-tool-row`(执行过程里的工具行)、`.blade-chat-agent-loop`(子智能体行) |
447
+ | 提问卡 | `.blade-chat-ask-card`、`.blade-chat-ask-option` |
448
+ | 计划与队列 | `.blade-chat-plan`、`.blade-chat-plan-block`、`.blade-chat-queue`、`.blade-chat-queue-item`、`.blade-chat-queue-notice` |
449
+ | 跟进区 | `.blade-chat-followup`、`.blade-chat-followup-action` |
450
+ | 输入区 | `.blade-chat-input`、`.blade-chat-input-inner`、`.blade-chat-textarea`、`.blade-chat-input-toolbar` |
451
+ | 上下文卡片 | `.blade-chat-context-card`、`.blade-chat-context-summary`、`.blade-chat-context-icon`、`.blade-chat-context-copy`、`.blade-chat-context-title`、`.blade-chat-context-status`、`.blade-chat-context-chevron`、`.blade-chat-context-detail`、`.blade-chat-context-group-items` |
452
+ | Markdown 与代码块 | `.blade-chat-markdown`、`.blade-chat-prose`、`.blade-chat-codeblock`、`.blade-chat-codeblock-header`、`.blade-chat-codeblock-body` |
453
+ | 其他 | `.blade-chat-host`(`<blade-chat>` 内的挂载点)、`.blade-chat-advanced`、`.blade-chat-mcp-app`、`.blade-chat-error-row`、`.blade-chat-error-block` |
454
+
455
+ ### part 清单(`<blade-chat>` 的 Shadow DOM 专用)
456
+
457
+ Shadow DOM 把上面的类名挡在页面之外,所以**主要区块**额外带一个与锚点同名的 `part`(去掉 `blade-chat-` 前缀;根容器的锚点是 `blade-chat`,约定用 `root`),页面外层用 `::part()` 选:
458
+
459
+ ```css
460
+ blade-chat::part(user-bubble) { background: #eef2ff; }
461
+ blade-chat::part(input-toolbar) { gap: 12px; }
462
+ ```
463
+
464
+ 清单(`src` 里每一处 `part` 都由构建校验,必须能对回锚点清单):
465
+
466
+ | 分组 | part |
467
+ | --- | --- |
468
+ | 容器与状态 | `root`、`banner`、`error-bar`、`replay-bar`、`mcp-app` |
469
+ | 消息区 | `messages`、`messages-scroll`、`messages-content`、`empty`、`scroll-bottom` |
470
+ | 用户消息 | `user-row`、`user-bubble`、`file-card`、`error-row` |
471
+ | 助手回复 | `assistant-turn`、`assistant-text`、`thinking`、`memory-refs` |
472
+ | 工具与提问 | `tool-row`、`agent-loop`、`ask-card`、`ask-option` |
473
+ | 计划与队列 | `plan`、`plan-block`、`queue` |
474
+ | 跟进区 | `followup`、`followup-action` |
475
+ | 输入区 | `input`、`input-inner`、`textarea`、`input-toolbar` |
476
+
477
+ 外围区块(`.blade-chat-context-*`、`.blade-chat-codeblock-*` 等)只有类名锚点,React 下可以直接选;`<blade-chat>` 下要改它们得用第 4 层的内嵌 `<style>`(那段样式就注入在 Shadow DOM 里)。
478
+
479
+ ### CSS 变量清单
480
+
481
+ 颜色是 `hsl()` 的三个分量(形如 `223 100% 54%`),几何是长度值。宿主盖掉这些变量即可整体换肤(React 下写在 `.blade-chat` 或祖先元素上;`<blade-chat>` 下写在元素上,变量能穿透 Shadow DOM):
482
+
483
+ | 分组 | 变量 |
484
+ | --- | --- |
485
+ | 基础色 | `--background`、`--foreground`、`--card`、`--card-foreground`、`--popover`、`--popover-foreground` |
486
+ | 语义色 | `--primary`、`--primary-foreground`、`--muted`、`--muted-foreground`、`--accent`、`--accent-foreground`、`--destructive`、`--border`、`--ring` |
487
+ | 用户消息专用 | `--user-msg-bg`、`--user-msg-fg`、`--user-msg-border` |
488
+ | 布局(SDK 按容器宽度推导) | `--blade-chat-gutter`(消息区与输入区的左右留白)、`--blade-chat-turn-gap`(轮次间距)。两个变量声明在 `.blade-chat` 上,**设在祖先元素上不生效**(元素上的声明永远赢过继承值)。React 下直接对 `.blade-chat` 写规则,或用 `classNames.root` 传一个设该变量的工具类(如 `"[--blade-chat-gutter:2rem]"`);`<blade-chat>` 下用第 4 层的内嵌 `<style>` |
489
+ | 布局(接入方按需覆盖) | `--blade-chat-queue-gutter`(队列面板左右留白,默认跟随 gutter)、`--blade-chat-pin-spacer`(消息顶置的底部补白,组件自己写)、`--blade-chat-input-top-border`(输入区上边框,组件自己写) |
490
+
491
+ 深色主题的变量挂在 `[data-theme="dark"]` 上,由 `theme` prop / 属性落到聊天根节点,整棵子树继承。
492
+
493
+ 上面这些是**会改变 SDK 组件外观**的变量。另外随主题一起声明的还有 `--radius`、`--input`、`--destructive-foreground`:`agent-react` 的默认组件不消费它们(圆角走 Tailwind 自己的 `--radius-*`),宿主在聊天容器里自绘 UI 时可以直接用,改动它们不会影响 SDK 自带的界面。
494
+
495
+ ### 区块级渲染器(renderers)
496
+
497
+ `classNames` 换不掉整个区块时用 `renderers`:每个渲染器拿到一个描述该区块的对象,返回 `ReactNode`;返回 `null` 走 SDK 默认渲染。
498
+
499
+ ```tsx
500
+ <ChatView
501
+ sessionId={id}
502
+ renderers={{
503
+ toolCall: (toolCall) => <MyToolCard toolCall={toolCall} />,
504
+ userMessage: ({ message }) => <MyBubble message={message} />,
505
+ assistantText: ({ message, streaming, compact }) => (compact ? null : <MyMarkdown text={message.content} />),
506
+ askUserQuestion: ({ data, answered, onAnswer }) => <MyQuestions data={data} answered={answered} onAnswer={onAnswer} />,
507
+ planUpdate: ({ plan, running }) => <MyPlanSteps plan={plan} running={running} />,
508
+ }}
509
+ />
510
+ ```
511
+
512
+ - `toolCall`:`(toolCall: ToolCallInfo) => ReactNode | null`,见 `ToolCallRenderer`。
513
+ - `userMessage`:`UserMessageRendererProps`——只替换用户消息气泡,消息行(`.blade-chat-user-row`)与图片/文件附件仍由 SDK 渲染;消息顶置依赖那个行容器,替换掉它新消息就不会被顶到视口顶部。
514
+ - `assistantText`:`AssistantTextRendererProps`——只替换助手正文,思考块、工具卡、附件照旧。
515
+ - `askUserQuestion`:`AskUserQuestionRendererProps`——`data` 是 `AskUserQuestionData`,`onAnswer` 与默认卡片同一个回调契约;照常作答才算完成这次提问。**提问工具优先走这个渲染器**,只有没配它时才回落到 `toolCall`(避免 `toolCall` 先拿到提问、而它没有作答回调)。
516
+ - `planUpdate`:`PlanUpdateRendererProps`——`plan` 是最近一次成功 `update_plan` 的解析结果;还没有解析出计划时渲染器不会被调用,回落到默认面板的「正在更新任务进度…」。
517
+
379
518
  ## 自定义消息列表滚动
380
519
 
381
520
  `ChatView`、`AgentChat` 和 `LlmChat` 已经处理好用户发言顶置,不用额外配置。自建消息列表时可用 `useMessagePin` 复用同一套触发、补白、视口 resize 和手动滚动接管逻辑;调用方只需提供滚动容器、目标消息与 spacer 的读写函数。完整参数见 [public-api.md](./public-api.md#usemessagepin-function)。
@@ -384,6 +523,22 @@ blade-chat { --primary: 262 83% 58%; height: 640px; }
384
523
 
385
524
  `ChatView`、`AgentChat` 和 `LlmChat` 已经内置这套节流,不用额外配置。服务端到达速率天然不均匀(一次推 5 个字、一次推 20 个字),直接原样渲染会显得忽快忽慢;`useTypewriterReveal(targetText, isLive, resetKey)` 按估算到达速度、留一个很小的固定滞后量匀速播放,返回 `{ displayedText, isRevealing, flushNow }`——自建消息列表时可以直接复用。`isLive` 传 `false`(历史加载 / 回放)时永远整段直接显示;`resetKey`(比如消息的 `entry_id`)变化时播放状态从头开始;`flushNow()` 用于用户点了停止时立即整段显示已到达内容。完整参数见 [public-api.md](./public-api.md#usetypewriterreveal-function)。
386
525
 
526
+ ## 长会话历史导航(useTurnNavigation / TurnNavRail)
527
+
528
+ `AgentChat` 已经内置这条轨道,不用额外配置:右侧刻度轨列出**整条会话**的用户发言(目录来自服务端
529
+ `/turn-index`,所以窗口外的旧发言也点得到),点已加载的发言直接滚动并短暂高亮,点窗口外的发言发
530
+ 一次定位请求(服务端返回「从它到最新」的窗口)后滚动到目标。
531
+
532
+ 自建聊天界面时用 `useTurnNavigation` 复用同一套时序(滚动 / 加载态 / 高亮):宿主提供
533
+ `TurnNavigationSource`(目录怎么来、定位怎么发、刷新信号)与 `getLoadedEntryElement` /
534
+ `revealEntry` / `beforeSeek` 三个列表侧回调(合起来就是 `TurnNavigationOptions`),返回
535
+ `TurnNavigationResult`(`items`、`loadingEntryId`、
536
+ `error`、`failedEntryId`、`activeEntryId`、`selectEntry`)。`TurnNavRail` 是配套的刻度轨组件
537
+ (`TurnNavRailProps`),样式与交互可直接用或照抄;定位高亮的类名是 `TURN_NAV_HIGHLIGHT_CLASS`。
538
+ 目录刷新信号用 `latestUserSpeechEntryId(turns)` 计算——只认最新一条用户发言,往前翻页补进的更早
539
+ 发言不该作废正在进行的定位;没有目录接口的会话(静态历史 / 分享页)用 `turnNavItemsFromMessages`
540
+ 把已加载的窗口转成兜底条目。
541
+
387
542
  ## 纯静态 HTML / Vue?
388
543
 
389
544
  不需要本包——用后端托管的单文件产物 `<script src=".../sdk/blade-agent.js">` + `<blade-chat>` 自定义元素,行为与 `ChatView` 一致;详见 agent-client README 与接入文档。
@@ -27385,7 +27385,7 @@ var Se2 = createContext({ code: "" });
27385
27385
  var de2 = () => useContext(Se2);
27386
27386
  var ot = ({ language: e }) => jsx("div", { className: "flex h-8 items-center text-muted-foreground text-xs", "data-language": e, "data-streamdown": "code-block-header", children: jsx("span", { className: "ml-1 font-mono lowercase", children: e }) });
27387
27387
  var cn2 = /\n+$/;
27388
- var dn2 = lazy(() => import("./highlighted-body-B3W2YXNL-IASCZDV2.js").then((e) => ({ default: e.HighlightedCodeBlockBody })));
27388
+ var dn2 = lazy(() => import("./highlighted-body-B3W2YXNL-YF4GVJGP.js").then((e) => ({ default: e.HighlightedCodeBlockBody })));
27389
27389
  var rt2 = ({ code: e, language: t, className: o, children: n, isIncomplete: s2 = false, ...r2 }) => {
27390
27390
  let i = useMemo(() => e.replace(cn2, ""), [e]), c = useMemo(() => ({ bg: "transparent", fg: "inherit", tokens: i.split(`
27391
27391
  `).map((a2) => [{ content: a2, color: "inherit", bgColor: "transparent", htmlStyle: {}, offset: 0 }]) }), [i]);
@@ -27732,7 +27732,7 @@ var Dt = ({ children: e, className: t, onDownload: o, onError: n }) => {
27732
27732
  }, []), jsxs("div", { className: "relative", ref: i, children: [jsx("button", { className: f2("cursor-pointer p-1 text-muted-foreground transition-all hover:text-foreground disabled:cursor-not-allowed disabled:opacity-50", t), disabled: c, onClick: () => r2(!s2), title: "Download table", type: "button", children: e != null ? e : jsx(Z3, { size: 14 }) }), s2 ? jsxs("div", { className: "absolute top-full right-0 z-10 mt-1 min-w-[120px] overflow-hidden rounded-md border border-border bg-background shadow-lg", children: [jsx("button", { className: "w-full px-3 py-2 text-left text-sm transition-colors hover:bg-muted/40", onClick: () => a2("csv"), title: "Download table as CSV", type: "button", children: "CSV" }), jsx("button", { className: "w-full px-3 py-2 text-left text-sm transition-colors hover:bg-muted/40", onClick: () => a2("markdown"), title: "Download table as Markdown", type: "button", children: "Markdown" })] }) : null] });
27733
27733
  };
27734
27734
  var Vt = ({ children: e, className: t, showControls: o, ...n }) => jsxs("div", { className: "my-4 flex flex-col gap-2 rounded-lg border border-border bg-sidebar p-2", "data-streamdown": "table-wrapper", children: [o ? jsxs("div", { className: "flex items-center justify-end gap-1", children: [jsx(Ht, {}), jsx(Dt, {})] }) : null, jsx("div", { className: "border-collapse overflow-x-auto overscroll-y-auto rounded-md border border-border bg-background", children: jsx("table", { className: f2("w-full divide-y divide-border", t), "data-streamdown": "table", ...n, children: e }) })] });
27735
- var Jn = lazy(() => import("./mermaid-3ZIDBTTL-BPVVWTVV.js").then((e) => ({ default: e.Mermaid })));
27735
+ var Jn = lazy(() => import("./mermaid-3ZIDBTTL-QL4YGD6I.js").then((e) => ({ default: e.Mermaid })));
27736
27736
  var Kn2 = /language-([^\s]+)/;
27737
27737
  function ke2(e, t) {
27738
27738
  if (!(e != null && e.position || t != null && t.position)) return true;
@@ -28222,10 +28222,11 @@ var Nt = ({ chart: e, className: t, config: o, fullscreen: n = false, showContro
28222
28222
 
28223
28223
  export {
28224
28224
  clsx,
28225
+ twMerge,
28225
28226
  Qe2 as Qe,
28226
28227
  Ks,
28227
28228
  R2 as R,
28228
28229
  _r,
28229
28230
  Nt
28230
28231
  };
28231
- //# sourceMappingURL=chunk-MAAMCOAU.js.map
28232
+ //# sourceMappingURL=chunk-IA6J2NTB.js.map
@@ -1,7 +1,8 @@
1
1
  import type { ToolCallInfo } from "@blade-hq/agent-client";
2
2
  interface Props {
3
3
  toolCall: ToolCallInfo;
4
+ className?: string;
4
5
  }
5
6
  /** SDK 没有第一方子循环消息流,只保留轻量意图行和可展开的最终结果。 */
6
- export declare function AgentLoopBlock({ toolCall }: Props): import("react/jsx-runtime").JSX.Element;
7
+ export declare function AgentLoopBlock({ toolCall, className }: Props): import("react/jsx-runtime").JSX.Element;
7
8
  export {};
@@ -23,9 +23,11 @@ interface Props {
23
23
  sessionStatus: string;
24
24
  answerData?: AskUserAnswerData | undefined;
25
25
  onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void;
26
+ className?: string;
27
+ optionClassName?: string;
26
28
  }
27
29
  /** 提问选项卡片:单选/多选 + 自定义输入,回答通过 onAnswer 回调交还 ChatView。 */
28
- export declare function AskUserQuestionBlock({ data, answered, toolCallId, sessionStatus, answerData, onAnswer, }: Props): import("react/jsx-runtime").JSX.Element;
30
+ export declare function AskUserQuestionBlock({ data, answered, toolCallId, sessionStatus, answerData, onAnswer, className, optionClassName, }: Props): import("react/jsx-runtime").JSX.Element;
29
31
  /** 解析 AskUserQuestion 的工具结果/参数,容错 LLM 把 questions 输出为字符串的情况。 */
30
32
  export declare function parseAskUserQuestion(toolResult: string | null | undefined): AskUserQuestionData | null;
31
33
  export interface AskUserQuestionError {
@@ -1,12 +1,15 @@
1
1
  import type { AskUserAnswerData, ChatMessage, MemoryRefInfo } from "@blade-hq/agent-client";
2
- import { type ToolCallRenderer } from "./ToolCallBlock";
2
+ import type { ChatViewClassNames, ChatViewRenderers } from "./ChatSurface";
3
3
  interface Props {
4
4
  messages: ChatMessage[];
5
5
  isStreaming?: boolean;
6
6
  askAnswers?: Record<string, AskUserAnswerData>;
7
7
  onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void;
8
8
  sessionStatus?: string;
9
- toolCallRenderer?: ToolCallRenderer;
9
+ /** 各可见区块的 className 落点;键与元素上的 `blade-chat-*` 锚点一一对应。 */
10
+ classNames?: ChatViewClassNames;
11
+ /** 区块级渲染器;返回 null 走默认渲染。 */
12
+ renderers?: ChatViewRenderers;
10
13
  /** Agent 模式把 update_plan 收口到输入框上方的任务面板;纯 LLM 同名工具照常展示。 */
11
14
  hidePlanUpdateTools?: boolean;
12
15
  /** 会话 id;传给 Markdown 渲染的统一上下文。 */
@@ -24,9 +27,10 @@ export declare function getExecutionDurationMs({ messages, isStreaming, now, }:
24
27
  now?: number;
25
28
  }): number;
26
29
  /** 一轮助手回复:执行过程默认折叠,最终正文始终显示在摘要下方。 */
27
- export declare function AssistantTurnBlock({ messages, isStreaming, askAnswers, onAnswer, sessionStatus, toolCallRenderer, hidePlanUpdateTools, sessionId, }: Props): import("react/jsx-runtime").JSX.Element;
30
+ export declare function AssistantTurnBlock({ messages, isStreaming, askAnswers, onAnswer, sessionStatus, classNames, renderers, hidePlanUpdateTools, sessionId, }: Props): import("react/jsx-runtime").JSX.Element;
28
31
  export declare function collectMemoryRefs(messages: ChatMessage[]): MemoryRefInfo[];
29
- export declare function MemoryRefsHint({ refs }: {
32
+ export declare function MemoryRefsHint({ refs, className }: {
30
33
  refs: MemoryRefInfo[];
34
+ className?: string;
31
35
  }): import("react/jsx-runtime").JSX.Element;
32
36
  export {};
@@ -20,6 +20,12 @@ interface Props {
20
20
  mergesWithPanelAbove?: boolean;
21
21
  placeholder?: string;
22
22
  className?: string;
23
+ /** 输入框卡片(`.blade-chat-input-inner`):边框、圆角、底色在这里。 */
24
+ innerClassName?: string;
25
+ /** 文本输入框(`.blade-chat-textarea`)。 */
26
+ textareaClassName?: string;
27
+ /** 右侧按钮组(`.blade-chat-input-toolbar`)。 */
28
+ toolbarClassName?: string;
23
29
  queueKey?: string;
24
30
  hasAttachments?: boolean;
25
31
  }
@@ -28,5 +34,5 @@ interface Props {
28
34
  * 不含第一方版本的富文本编辑、@ 补全、技能菜单、语音输入与模型选择。
29
35
  * 内容受控,session.attach() / insertText() 通过 ChatView 注入到这里。
30
36
  */
31
- export declare function ChatInput({ value, onValueChange, onSend, onStop, isStreaming, isStopping, queueWhileRunning, mergesWithPanelAbove, placeholder, className, queueKey, hasAttachments, }: Props): import("react/jsx-runtime").JSX.Element;
37
+ export declare function ChatInput({ value, onValueChange, onSend, onStop, isStreaming, isStopping, queueWhileRunning, mergesWithPanelAbove, placeholder, className, innerClassName, textareaClassName, toolbarClassName, queueKey, hasAttachments, }: Props): import("react/jsx-runtime").JSX.Element;
32
38
  export {};
@@ -1,17 +1,119 @@
1
1
  import type { AskUserAnswerData, ChatMessage, ConnectionStatus, PostChatFollowup } from "@blade-hq/agent-client";
2
2
  import { type ReactNode } from "react";
3
+ import type { TurnNavigationSource } from "../hooks/use-turn-navigation";
3
4
  import type { ToolCallRenderer } from "./ToolCallBlock";
4
5
  import type { FollowupInteractionEvent } from "./PostChatFollowupBlock";
5
6
  import type { ResultFeedback } from "@blade-hq/agent-client";
7
+ import { type PlanUpdateData } from "./PlanUpdateBlock";
8
+ import type { AskUserQuestionData } from "./AskUserQuestionBlock";
9
+ /**
10
+ * 每个可见区块的 className 落点,键名与元素上的 `blade-chat-*` 语义锚点一一对应。
11
+ *
12
+ * 传进来的类会与 SDK 自己的类一起经过 `cn`(clsx + tailwind-merge),放在
13
+ * 最后一个参数,因此 `classNames={{ chatInput: "p-0" }}` 能盖掉组件内部的 `p-4`,
14
+ * 不依赖 CSS 输出顺序、也不需要 `!important`。锚点本身不带视觉规则(清单见
15
+ * README「样式定制」与 src/style.css 顶部),用普通 CSS 选它们同样稳定生效。
16
+ */
6
17
  export interface ChatViewClassNames {
18
+ /** 聊天根容器(`.blade-chat`)。 */
7
19
  root?: string;
20
+ /** 连接状态提示条(`.blade-chat-banner`)。 */
8
21
  banner?: string;
22
+ /** 顶部出错条(`.blade-chat-error-bar`)。 */
23
+ errorBar?: string;
24
+ /** 消息区(`.blade-chat-messages`)。 */
9
25
  messageList?: string;
26
+ /** 消息区滚动内容(`.blade-chat-messages-scroll`):内边距在这里。 */
27
+ messagesScroll?: string;
28
+ /** 消息内容列(`.blade-chat-messages-content`):居中与最大宽度在这里。 */
29
+ messagesContent?: string;
30
+ /** 无消息时的空状态(`.blade-chat-empty`)。 */
31
+ empty?: string;
32
+ /** 「滚动到底部」按钮(`.blade-chat-scroll-bottom`)。 */
33
+ scrollToBottom?: string;
34
+ /** 用户消息行(`.blade-chat-user-row`)。 */
35
+ userMessage?: string;
36
+ /** 用户气泡(`.blade-chat-user-bubble`)。 */
37
+ userBubble?: string;
38
+ /** 消息里的文件附件卡(`.blade-chat-file-card`)。 */
39
+ fileCard?: string;
40
+ /** 消息流里的出错块(`.blade-chat-error-row`)。 */
41
+ errorMessage?: string;
42
+ /** 一轮助手回复(`.blade-chat-assistant-turn`)。 */
43
+ assistantTurn?: string;
44
+ /** 思考过程(`.blade-chat-thinking`)。 */
45
+ thinking?: string;
46
+ /** 助手正文(`.blade-chat-assistant-text`)。 */
47
+ assistantText?: string;
48
+ /** 记忆引用(`.blade-chat-memory-refs`)。 */
49
+ memoryRefs?: string;
50
+ /** 执行过程里的工具行(`.blade-chat-tool-row`):智能体模式下工具调用都走这条路径。 */
51
+ toolRow?: string;
52
+ /** 子智能体行(`.blade-chat-agent-loop`)。 */
53
+ agentLoop?: string;
54
+ /** 提问卡(`.blade-chat-ask-card`)。 */
55
+ askCard?: string;
56
+ /** 提问卡的每个选项(`.blade-chat-ask-option`)。 */
57
+ askOption?: string;
58
+ /** 计划面板(`.blade-chat-plan`)。 */
59
+ planPanel?: string;
60
+ /** 会话队列面板(`.blade-chat-queue`)。 */
61
+ queuePanel?: string;
62
+ /** 本轮小结与跟进区(`.blade-chat-followup`)。 */
63
+ followup?: string;
64
+ /** 跟进建议按钮(`.blade-chat-followup-action`)。 */
65
+ followupAction?: string;
66
+ /** 输入区外层(`.blade-chat-input`)。 */
10
67
  chatInput?: string;
68
+ /** 输入框卡片(`.blade-chat-input-inner`):边框、圆角、底色在这里。 */
69
+ inputInner?: string;
70
+ /** 文本输入框(`.blade-chat-textarea`)。 */
71
+ textarea?: string;
72
+ /** 输入框右侧按钮组(`.blade-chat-input-toolbar`)。 */
73
+ inputToolbar?: string;
74
+ }
75
+ /** 用户消息渲染器:`message` 是投影层给出的用户消息。 */
76
+ export interface UserMessageRendererProps {
77
+ message: ChatMessage;
11
78
  }
79
+ /** 助手正文渲染器:`compact` 为真时正文在执行过程里,字号更小。 */
80
+ export interface AssistantTextRendererProps {
81
+ message: ChatMessage;
82
+ streaming: boolean;
83
+ compact: boolean;
84
+ sessionId?: string;
85
+ }
86
+ /** 提问卡渲染器:字段与 `AskUserQuestionBlock` 的 props 一致。 */
87
+ export interface AskUserQuestionRendererProps {
88
+ data: AskUserQuestionData;
89
+ answered: boolean;
90
+ toolCallId: string;
91
+ sessionStatus: string;
92
+ answerData?: AskUserAnswerData;
93
+ onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void;
94
+ }
95
+ /** 计划面板渲染器:`plan` 是最近一次成功的 update_plan 解析结果。 */
96
+ export interface PlanUpdateRendererProps {
97
+ plan: PlanUpdateData;
98
+ running: boolean;
99
+ sessionId?: string;
100
+ }
101
+ /**
102
+ * 区块级渲染器:每个都返回 `ReactNode`,返回 `null` 走 SDK 默认渲染。
103
+ *
104
+ * 与 `toolCall` 的老签名一致:参数是一个描述该区块的对象,而不是裸数据。
105
+ */
12
106
  export interface ChatViewRenderers {
13
107
  /** 自定义工具调用渲染;返回 null 走默认渲染。 */
14
108
  toolCall?: ToolCallRenderer;
109
+ /** 自定义用户消息渲染;返回 null 走默认渲染。 */
110
+ userMessage?: (props: UserMessageRendererProps) => ReactNode | null;
111
+ /** 自定义助手正文渲染;返回 null 走默认渲染。 */
112
+ assistantText?: (props: AssistantTextRendererProps) => ReactNode | null;
113
+ /** 自定义提问卡渲染;返回 null 走默认渲染。 */
114
+ askUserQuestion?: (props: AskUserQuestionRendererProps) => ReactNode | null;
115
+ /** 自定义计划面板渲染;返回 null 走默认渲染。 */
116
+ planUpdate?: (props: PlanUpdateRendererProps) => ReactNode | null;
15
117
  }
16
118
  export interface ChatViewSlots {
17
119
  /** 消息列表上方的自定义区域。 */
@@ -75,6 +177,8 @@ interface ChatSurfaceProps extends ChatPresentationProps {
75
177
  /** Resolves true only when the paging cursor advanced. */
76
178
  loadOlder: (beforeCommit?: () => void) => Promise<boolean>;
77
179
  };
180
+ /** 消息导航的数据源(目录 + 定位);不传就没有导航轨道。 */
181
+ turnNavigation?: TurnNavigationSource;
78
182
  /** 连接状态条下方的横幅(回放模式条用)。不传时不渲染任何节点。 */
79
183
  banner?: ReactNode;
80
184
  /**
@@ -91,5 +195,5 @@ interface ChatSurfaceProps extends ChatPresentationProps {
91
195
  * 这里的 DOM 结构受 tests/chat-view-compat.test.tsx 的快照保护——接入方拿
92
196
  * .blade-chat-* 类名写了自己的样式,结构一动他们的页面就歪。
93
197
  */
94
- export declare function ChatSurface({ theme, classNames, renderers, slots, placeholder, connection, errorMessage, messages, postChatFollowup, isStreaming, isStopping, inputText, onInputChange, onSuggestion, onSend, onStop, sessionStatus, askAnswers, onAnswer, sessionId, isViewer, resultFeedbackByEntry, onResultFeedbackSaved, onFollowupInteraction, beforeInput, queuePanel, queuePanelVisible, showPlanUpdates, planRevealRevision, historyPaging, banner, onRevealingChange, isReplay, }: ChatSurfaceProps): import("react/jsx-runtime").JSX.Element;
198
+ export declare function ChatSurface({ theme, classNames, renderers, slots, placeholder, connection, errorMessage, messages, postChatFollowup, isStreaming, isStopping, inputText, onInputChange, onSuggestion, onSend, onStop, sessionStatus, askAnswers, onAnswer, sessionId, isViewer, resultFeedbackByEntry, onResultFeedbackSaved, onFollowupInteraction, beforeInput, queuePanel, queuePanelVisible, showPlanUpdates, planRevealRevision, historyPaging, turnNavigation, banner, onRevealingChange, isReplay, }: ChatSurfaceProps): import("react/jsx-runtime").JSX.Element;
95
199
  export {};
@@ -2,7 +2,7 @@ import { type AgentChatProps } from "./AgentChat";
2
2
  import { type LlmChatHandle } from "./LlmChat";
3
3
  import type { LlmAdvancedSettings, LlmOverride } from "./LlmAdvancedSettings";
4
4
  import type { LlmChatOptions } from "../hooks/use-llm-chat";
5
- export type { ChatViewClassNames, ChatViewRenderers, ChatViewSlots, } from "./ChatSurface";
5
+ export type { AssistantTextRendererProps, AskUserQuestionRendererProps, ChatViewClassNames, ChatViewRenderers, ChatViewSlots, PlanUpdateRendererProps, UserMessageRendererProps, } from "./ChatSurface";
6
6
  export type { FollowupInteractionEvent } from "./PostChatFollowupBlock";
7
7
  export interface ChatViewProps extends AgentChatProps {
8
8
  /** 挂哪个下层组件。默认 `agent`——不传就是接 Blade 智能体会话的原有行为。 */
@@ -1,7 +1,8 @@
1
1
  import type { AskUserAnswerData, ChatMessage, PostChatFollowup, ResultFeedback } from "@blade-hq/agent-client";
2
2
  import { type ReactNode } from "react";
3
+ import { type TurnNavigationSource } from "../hooks/use-turn-navigation";
3
4
  import { type FollowupInteractionEvent } from "./PostChatFollowupBlock";
4
- import type { ToolCallRenderer } from "./ToolCallBlock";
5
+ import type { ChatViewClassNames, ChatViewRenderers } from "./ChatSurface";
5
6
  interface Props {
6
7
  messages: ChatMessage[];
7
8
  postChatFollowup?: PostChatFollowup | null;
@@ -10,10 +11,13 @@ interface Props {
10
11
  sessionStatus?: string;
11
12
  askAnswers?: Record<string, AskUserAnswerData>;
12
13
  onAnswer?: (answer: string, toolCallId: string, answerData: AskUserAnswerData) => void;
13
- toolCallRenderer?: ToolCallRenderer;
14
14
  hidePlanUpdateTools?: boolean;
15
+ /** 无消息时替换默认空状态。 */
15
16
  emptyState?: ReactNode;
16
- className?: string;
17
+ /** 各可见区块的 className 落点;键与元素上的 `blade-chat-*` 锚点一一对应。 */
18
+ classNames?: ChatViewClassNames;
19
+ /** 区块级渲染器;返回 null 走默认渲染。 */
20
+ renderers?: ChatViewRenderers;
17
21
  /** 会话 id;传给助手消息的统一上下文。 */
18
22
  sessionId?: string;
19
23
  isViewer?: boolean;
@@ -26,6 +30,11 @@ interface Props {
26
30
  /** Resolves true only when the paging cursor advanced. */
27
31
  loadOlder: (beforeCommit?: () => void) => Promise<boolean>;
28
32
  };
33
+ /**
34
+ * 消息导航的数据源(目录 + 定位)。不传就没有导航轨道,也不会请求目录——
35
+ * 纯 LLM 会话与自带入口的宿主都属于这种。
36
+ */
37
+ turnNavigation?: TurnNavigationSource;
29
38
  /**
30
39
  * 用户点了停止(还没等服务端确认)——打字机式显示节流要在这一刻立刻整段
31
40
  * 显示已到达内容,不能等 chat:stop 的服务端回包才响应。跟 isStreaming
@@ -49,5 +58,5 @@ interface Props {
49
58
  * 消息列表:use-stick-to-bottom 自动滚动 + 按轮次分组渲染。
50
59
  * 相比第一方版本去掉了轮次导航栏、吸顶状态条与规划摘要卡片。
51
60
  */
52
- export declare function MessageList({ messages, postChatFollowup, onSuggestion, isStreaming, sessionStatus, askAnswers, onAnswer, toolCallRenderer, hidePlanUpdateTools, emptyState, className, sessionId, isViewer, onFollowupInteraction, resultFeedbackByEntry, onResultFeedbackSaved, historyPaging, isStopping, onRevealingChange, isReplay, }: Props): import("react/jsx-runtime").JSX.Element;
61
+ export declare function MessageList({ messages, postChatFollowup, onSuggestion, isStreaming, sessionStatus, askAnswers, onAnswer, hidePlanUpdateTools, emptyState, classNames, renderers, sessionId, isViewer, onFollowupInteraction, resultFeedbackByEntry, onResultFeedbackSaved, historyPaging, turnNavigation, isStopping, onRevealingChange, isReplay, }: Props): import("react/jsx-runtime").JSX.Element;
53
62
  export {};
@@ -15,11 +15,12 @@ export interface PlanUpdateDisplayState {
15
15
  export declare function getPlanUpdateDisplayState(messages: ChatMessage[]): PlanUpdateDisplayState;
16
16
  export declare function parsePlanUpdate(argumentsJson: string): PlanUpdateData | null;
17
17
  export declare function pickCurrentPlanStep(plan: PlanUpdateData["plan"]): PlanUpdateData["plan"][number] | null;
18
- export declare function PlanUpdateBlock({ toolCall, running, autoReveal, }: {
18
+ export declare function PlanUpdateBlock({ toolCall, running, autoReveal, className, }: {
19
19
  toolCall: ToolCallInfo;
20
20
  running?: boolean;
21
21
  /** 实时收到的新进度短暂展开;加载历史时保持默认收起。 */
22
22
  autoReveal?: boolean;
23
+ className?: string;
23
24
  }): import("react/jsx-runtime").JSX.Element | null;
24
25
  export declare function CurrentPlanPanel({ messages, running, revealRevision, sessionId, className, }: {
25
26
  messages: ChatMessage[];
@@ -48,7 +48,7 @@ export type FollowupInteractionEvent = {
48
48
  export declare function HistoricalResultFeedback({ feedback }: {
49
49
  feedback: SavedResultFeedback;
50
50
  }): import("react/jsx-runtime").JSX.Element;
51
- export declare function PostChatFollowupBlock({ followup, sessionId, onSuggestion, isViewer, onInteraction, savedFeedback, onFeedbackSaved, }: {
51
+ export declare function PostChatFollowupBlock({ followup, sessionId, onSuggestion, isViewer, onInteraction, savedFeedback, onFeedbackSaved, className, suggestionClassName, }: {
52
52
  followup: PostChatFollowup;
53
53
  sessionId?: string;
54
54
  onSuggestion?: (value: string) => void;
@@ -56,4 +56,8 @@ export declare function PostChatFollowupBlock({ followup, sessionId, onSuggestio
56
56
  onInteraction?: (event: FollowupInteractionEvent) => void;
57
57
  savedFeedback?: SavedResultFeedback;
58
58
  onFeedbackSaved?: (feedback: SavedResultFeedback) => void;
59
+ /** 追加到跟进区根节点的类(`ChatViewClassNames.followup`)。 */
60
+ className?: string;
61
+ /** 追加到每个「下一步」建议按钮的类(`ChatViewClassNames.followupAction`)。 */
62
+ suggestionClassName?: string;
59
63
  }): import("react/jsx-runtime").JSX.Element | null;
@@ -1,5 +1,6 @@
1
1
  import type { AskUserAnswerData, ToolCallInfo } from "@blade-hq/agent-client";
2
2
  import { type ReactNode } from "react";
3
+ import type { ChatViewClassNames, ChatViewRenderers } from "./ChatSurface";
3
4
  /** 接入方自定义渲染器:返回 null 时回落到默认渲染。 */
4
5
  export type ToolCallRenderer = (info: ToolCallInfo) => ReactNode | null;
5
6
  interface Props {
@@ -9,7 +10,10 @@ interface Props {
9
10
  answerData?: AskUserAnswerData;
10
11
  sessionStatus?: string;
11
12
  isActiveQuestion?: boolean;
12
- renderer?: ToolCallRenderer;
13
+ /** 各可见区块的 className 落点;键与元素上的 `blade-chat-*` 锚点一一对应。 */
14
+ classNames?: ChatViewClassNames;
15
+ /** 区块级渲染器;返回 null 走默认渲染。 */
16
+ renderers?: ChatViewRenderers;
13
17
  }
14
18
  /**
15
19
  * 判断提问卡片该等待作答还是展示成已作答,只认工具状态。
@@ -33,5 +37,5 @@ export declare function resolveAskQuestionState({ toolStatus, hasAnswerData, fal
33
37
  * 极简工具调用块:状态 + 展示名一行,点开后是参数/结果的 <pre> 折叠展示。
34
38
  * AskUserQuestion 特殊处理为提问选项卡片。
35
39
  */
36
- export declare function ToolCallBlock({ toolCall, onAnswer, answered, answerData, sessionStatus, isActiveQuestion, renderer, }: Props): import("react/jsx-runtime").JSX.Element;
40
+ export declare function ToolCallBlock({ toolCall, onAnswer, answered, answerData, sessionStatus, isActiveQuestion, classNames, renderers, }: Props): import("react/jsx-runtime").JSX.Element;
37
41
  export {};
@@ -0,0 +1,23 @@
1
+ import type { TurnNavItem } from "../hooks/use-turn-navigation";
2
+ export interface TurnNavRailProps {
3
+ /** 当前会话全部用户发言的目录;少于两条时不渲染(一条没有导航的意义)。 */
4
+ items: TurnNavItem[];
5
+ /** 正在高亮的那条发言(视口所在轮,或刚定位到的目标)。 */
6
+ activeTurnId: string | null;
7
+ /** 正在请求定位的目标;null 表示没有在途定位。 */
8
+ loadingTurnId?: string | null;
9
+ /** 定位失败提示。 */
10
+ error?: string | null;
11
+ /** 定位失败的那条发言;重试按钮就再点它一次。 */
12
+ failedTurnId?: string | null;
13
+ /** 点击/重试某条发言。 */
14
+ onSelectTurn: (entryId: string) => void;
15
+ }
16
+ /**
17
+ * 用户发言的刻度轨:右侧一列刻度,悬停/聚焦展开目录列表。
18
+ *
19
+ * 只负责显示和点击,请求、滚动与高亮全在 `useTurnNavigation` 里——内置 Web 那份
20
+ * rail(apps/web 的 TurnNavRail.tsx)有同样的语义,样式与交互各自实现。
21
+ * 类名走 `blade-turn-nav-*`:宿主没装 Tailwind 时 style.css 提供等效样式。
22
+ */
23
+ export declare function TurnNavRail({ items, activeTurnId, loadingTurnId, error, failedTurnId, onSelectTurn, }: TurnNavRailProps): import("react/jsx-runtime").JSX.Element | null;
@@ -1,4 +1,5 @@
1
1
  import type { ChatMessage } from "@blade-hq/agent-client";
2
+ import type { ChatViewClassNames, ChatViewRenderers } from "./ChatSurface";
2
3
  export type UserChatMessage = ChatMessage & {
3
4
  role: "user";
4
5
  };
@@ -9,10 +10,13 @@ export declare function isUserMessage(message: ChatMessage): message is UserChat
9
10
  export declare function isErrorMessage(message: ChatMessage): message is ErrorChatMessage;
10
11
  interface UserMessageProps {
11
12
  message: UserChatMessage;
12
- className?: string;
13
+ /** 各可见区块的 className 落点;键与元素上的 `blade-chat-*` 锚点一一对应。 */
14
+ classNames?: ChatViewClassNames;
15
+ /** 区块级渲染器;userMessage 只替换气泡本身,行与附件仍由 SDK 渲染。 */
16
+ renderers?: ChatViewRenderers;
13
17
  }
14
18
  /** 用户消息气泡:文本、canonical 引用消息与图片/文件附件。 */
15
- export declare function UserMessageBubble({ message, className }: UserMessageProps): import("react/jsx-runtime").JSX.Element;
19
+ export declare function UserMessageBubble({ message, classNames, renderers }: UserMessageProps): import("react/jsx-runtime").JSX.Element;
16
20
  /** 错误消息:居中红色提示条。 */
17
21
  export declare function ErrorMessageBlock({ message, className, }: {
18
22
  message: ErrorChatMessage;
@@ -18,9 +18,10 @@ export declare class BladeChatElement extends HTMLElement {
18
18
  connectedCallback(): void;
19
19
  /**
20
20
  * 自定义样式口子:<blade-chat> 的直接 <style> 子元素会被拷入 Shadow DOM,
21
- * 排在内置样式之后(同优先级时后者胜)。配合内置的 .blade-chat-* 类名
22
- * CSS 变量(--primary / --background 等,页面直接对 blade-chat 元素
23
- * 设置即可穿透),无需构建工具即可深度定制。
21
+ * 排在内置样式之后(同优先级时后者胜)。配合内置的 .blade-chat-* 类名、
22
+ * ::part()(每个可见区块都带与锚点同名的 part,如
23
+ * blade-chat::part(user-bubble))与 CSS 变量(--primary / --background 等,
24
+ * 页面直接对 blade-chat 元素设置即可穿透),无需构建工具即可深度定制。
24
25
  */
25
26
  private adoptUserStyles;
26
27
  disconnectedCallback(): void;