AI 会话
AI 会话容器(AI 族门面条目):滚动钉底跟随、上滚暂停跟随并浮出「回到底部 · N 条」浮动钮(缺失自动补建);消息 copy / retry 操作委托(icen:ai-copy / icen:ai-retry);reasoning 折叠委托。配套 createAiStream 做 textContent 级流式追加;data-density 三档(verbose / normal / summary)控制信息密度。下方 demo 是全链路实况:composer v2(模型切换//命令/@引用/上下文环)→ createAiClient(mock provider,SSE 流式)→ createAiStream 渲染 → 审计聚合 → 用量环累计。
我
帮我梳理这次发布前的检查清单,重点是构建验证和回滚路径。
AI
好,先跑一下构建确认基线。
用户关注发布风险,清单需要覆盖:构建是否全绿、类型检查、关键页面冒烟、回滚路径。本次发布有 tag,回滚安全,可以先给结论。
输入
bun run build
输出
tsup ✓ · build-css: 55 components + 75 kit 入口 → dist/
AI
<div class="toolbar" style="margin-bottom:10px"> <span class="toolbar-label">密度</span> <button class="btn btn-sm" type="button" data-ai-density="verbose">verbose</button> <button class="btn btn-sm btn-primary" type="button" data-ai-density="normal">normal</button> <button class="btn btn-sm" type="button" data-ai-density="summary">summary</button> <span class="toolbar-spacer"></span> <span class="toolbar-label">发消息试试:全链路 demo(mock provider 流式回包 + 用量环累计)</span></div><div class="ai-chat" id="ai-chat-demo" data-density="normal" style="height:380px;border:1px solid var(--token-line-soft);border-radius:var(--radius-md);padding:10px"> <div class="ai-chat-scroll"> <div class="ai-msg ai-msg--user"> <span class="ai-msg-avatar">我</span> <div style="min-width:0"> <div class="ai-msg-body">帮我梳理这次发布前的检查清单,重点是构建验证和回滚路径。</div> <div class="ai-msg-meta">12:04 · 28 tok</div> </div> </div> <div class="ai-msg ai-msg--assistant"> <span class="ai-msg-avatar">AI</span> <div style="min-width:0"> <div class="ai-msg-body">好,先跑一下构建确认基线。</div> </div> </div> <div class="ai-reasoning is-done"> <button class="ai-reasoning-head" type="button" aria-expanded="false"> <span class="ai-reasoning-label">思考过程</span><span class="ai-reasoning-time">2.1s</span> </button> <div class="ai-reasoning-body" hidden>用户关注发布风险,清单需要覆盖:构建是否全绿、类型检查、关键页面冒烟、回滚路径。本次发布有 tag,回滚安全,可以先给结论。</div> </div> <div class="ai-tool ai-tool--shell is-done" data-ai-id="demo-chat-shell" data-ai-kind="shell"> <button class="ai-tool-head" type="button" aria-expanded="false"> <span class="ai-item-icon" aria-hidden="true"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m4 17 6-6-6-6"/><path d="M12 19h8"/></svg></span> <span class="ai-item-main"> <span class="ai-item-title">Shell</span> <span class="ai-item-sub">bun run build</span> </span> <span class="ai-item-status" role="img" aria-label="完成"></span> <span class="ai-item-meta">3.2s</span> </button> <div class="ai-tool-body" hidden> <div class="ai-tool-io"><div class="ai-tool-io-label">输入</div><pre class="ai-tool-io-content">bun run build</pre></div> <div class="ai-tool-io"><div class="ai-tool-io-label">输出</div><pre class="ai-tool-io-content">tsup ✓ · build-css: 55 components + 75 kit 入口 → dist/</pre></div> </div> </div> <div class="ai-msg ai-msg--assistant" id="ai-chat-last" hidden> <span class="ai-msg-avatar">AI</span> <div style="min-width:0"> <div class="ai-msg-body" id="ai-chat-stream"></div> <div class="ai-msg-meta" id="ai-chat-meta" hidden></div> </div> </div> </div></div><div class="ai-composer" data-ai-composer id="ai-chat-composer" style="margin-top:10px"> <div class="ai-composer-queue" hidden></div> <div class="ai-composer-attach" hidden></div> <div class="ai-composer-box control"> <textarea class="ai-composer-input" rows="1" placeholder="输入消息,/ 命令 · @ 引用 · Enter 发送"></textarea> <div class="ai-composer-actions"> <button class="ai-composer-btn" data-ai-attach type="button" aria-label="附件"></button> <button class="ai-composer-send" data-ai-send type="button" aria-label="发送"></button> </div> </div></div> 安装
bun add @icen.ai/ui pnpm add @icen.ai/ui npm install @icen.ai/ui yarn add @icen.ai/ui 一行引入该组件(CSS 与 behavior 自动带上,无需关心内部 CSS 文件名):
import { initAiChat } from '@icen.ai/ui/kit/ai-chat';initAiChat(); 也可以用脚手架直接打印引入行:
bunx --bun @icen.ai/ui add ai-chat
Astro 项目注意:kit 入口里的 CSS import 在页面 <script> 里会被 Astro 的 client bundle 摇掉——请改在布局 frontmatter 里引 CSS(import '@icen.ai/ui/components/ai-chat.css';),JS 行为仍可走 kit。Vite SPA / webpack 项目无此问题。
用法
import { initAiChat, createAiStream } from '@icen.ai/ui/kit/ai-chat';initAiChat(); // 钉底跟随 / 上滚暂停 / .ai-chat-jump 缺失自动补建 / copy·retry / reasoning 折叠 <!-- DOM 契约 --><div class="ai-chat" data-density="normal"> <div class="ai-chat-scroll"> <div class="ai-msg ai-msg--user"> <span class="ai-msg-avatar">我</span> <div> <div class="ai-msg-body">…消费方自行渲染 markdown…</div> <div class="ai-msg-meta">12:04 · 1.2k tok</div> </div> </div> </div> <button class="ai-chat-jump" hidden>回到底部 · 3 条新消息</button></div> // 流式回复:textContent 级追加(不解析 HTML;markdown 重渲染是消费方职责)const s = createAiStream(document.querySelector('.ai-msg-body'));s.append('…');s.done(); // 或 s.cancel()