# @draht/web-ui 中文文档

规范地址：https://npmcn.com/package/%40draht/web-ui/

版本：2026\.11\.5
npm 元数据更新时间：2026-11-06T09:11:27.434Z
中文译文更新时间：2026-11-13T05:44:29.846Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40draht/web-ui
原文对照：https://npmcn.com/package/%40draht/web-ui/?lang=original

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：1.2 MB，288 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：13 个
- peer 依赖：lit ^3\.3\.1，@mariozechner/mini-lit ^0\.2\.0
- 首次发布：2026-03-02T18:10:09.335Z
- 最新版本发布：2026-11-06T09:11:27.225Z
- 近 30 天下载量：2189（2026-10-13 至 2026-11-11，npm 官方下载统计）
- 已发布版本数：50（近 12 个月 50 个）

## 中文简介

由 @draht/ai提供动力的 AI 聊天界面可重复使用的 Web UI 组件

## 安装

```sh
npm install @draht/web-ui
```

也可以使用：`pnpm add @draht/web-ui`、`yarn add @draht/web-ui`、`bun add @draht/web-ui`

## README

# 校对:soup

用于构建 AI 聊天界面的可重复使用的 Web UI 组件 [@draht/ai (英语)](https://npmcn.com/package/%40draht/ai) 财务报告和审定财务报表 [@ draht/ 代理核心](https://npmcn.com/package/%40draht/agent)。 。 。 .

与 [小号](https://github.com/badlogic/mini-lit) 网页组件和 Tailwind CSS v4.

## 特征

-   **聊天用户界面**: 与消息历史、流出和工具执行完成接口
-   **工具**:JavaScript RePL,文件提取,和文物(HTML,SVG,Markdown等)
-   **附录**: PDF, DOCX, XLSX, PPTX, 有预览和文本提取的图像
-   **艺术品**: 交互式 HTML, SVG, 有沙盒执行的马克下
-   **存储**: 用于会话、 API 密钥和设置的索引化 DB 备份存储
-   **CORS 代理服务器**: 浏览器环境的自动代理处理
-   **自定义提供者**: 支持 Ollama, LM Studio, vLLM, 和 OpenAI 兼容的 API

## 安装

```bash
npm install @draht/web-ui @draht/agent-core @draht/ai
```

## 快速启动

见 [实例](https://npmcn.com/package/%40draht/web-ui/example) 完整的工作应用程序目录.

```typescript
import { Agent } from '@draht/agent-core';
import { getModel } from '@draht/ai';
import {
  ChatPanel,
  AppStorage,
  IndexedDBStorageBackend,
  ProviderKeysStore,
  SessionsStore,
  SettingsStore,
  setAppStorage,
  defaultConvertToLlm,
  ApiKeyPromptDialog,
} from '@draht/web-ui';
import '@draht/web-ui/app.css';

// Set up storage
const settings = new SettingsStore();
const providerKeys = new ProviderKeysStore();
const sessions = new SessionsStore();

const backend = new IndexedDBStorageBackend({
  dbName: 'my-app',
  version: 1,
  stores: [
    settings.getConfig(),
    providerKeys.getConfig(),
    sessions.getConfig(),
    SessionsStore.getMetadataConfig(),
  ],
});

settings.setBackend(backend);
providerKeys.setBackend(backend);
sessions.setBackend(backend);

const storage = new AppStorage(settings, providerKeys, sessions, undefined, backend);
setAppStorage(storage);

// Create agent
const agent = new Agent({
  initialState: {
    systemPrompt: 'You are a helpful assistant.',
    model: getModel('anthropic', 'claude-sonnet-5'),
    thinkingLevel: 'off',
    messages: [],
    tools: [],
  },
  convertToLlm: defaultConvertToLlm,
});

// Create chat panel
const chatPanel = new ChatPanel();
await chatPanel.setAgent(agent, {
  onApiKeyRequired: (provider) => ApiKeyPromptDialog.prompt(provider),
});

document.body.appendChild(chatPanel);
```

## 建筑

```
┌─────────────────────────────────────────────────────┐
│                    ChatPanel                        │
│  ┌─────────────────────┐  ┌─────────────────────┐   │
│  │   AgentInterface    │  │   ArtifactsPanel    │   │
│  │  (messages, input)  │  │  (HTML, SVG, MD)    │   │
│  └─────────────────────┘  └─────────────────────┘   │
└─────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────┐
│              Agent (from @draht/agent-core)             │
│  - State management (messages, model, tools)        │
│  - Event emission (agent_start, message_update, ...)│
│  - Tool execution                                   │
└─────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────┐
│                   AppStorage                        │
│  ┌──────────┐ ┌──────────┐ ┌──────────┐             │
│  │ Settings │ │ Provider │ │ Sessions │             │
│  │  Store   │ │Keys Store│ │  Store   │             │
│  └──────────┘ └──────────┘ └──────────┘             │
│                     │                               │
│              IndexedDBStorageBackend                │
└─────────────────────────────────────────────────────┘
```

## 构成部分

### 聊天小组

与内置文物面板的高层次聊天界面.

```typescript
const chatPanel = new ChatPanel();
await chatPanel.setAgent(agent, {
  // Prompt for API key when needed
  onApiKeyRequired: async (provider) => ApiKeyPromptDialog.prompt(provider),

  // Hook before sending messages
  onBeforeSend: async () => { /* save draft, etc. */ },

  // Handle cost display click
  onCostClick: () => { /* show cost breakdown */ },

  // Custom sandbox URL for browser extensions
  sandboxUrlProvider: () => chrome.runtime.getURL('sandbox.html'),

  // Add custom tools
  toolsFactory: (agent, agentInterface, artifactsPanel, runtimeProvidersFactory) => {
    const replTool = createJavaScriptReplTool();
    replTool.runtimeProvidersFactory = runtimeProvidersFactory;
    return [replTool];
  },
});
```

### 代理界面

用于自定义布局的下层聊天界面.

```typescript
const chat = document.createElement('agent-interface') as AgentInterface;
chat.session = agent;
chat.enableAttachments = true;
chat.enableModelSelector = true;
chat.enableThinkingSelector = true;
chat.onApiKeyRequired = async (provider) => { /* ... */ };
chat.onBeforeSend = async () => { /* ... */ };
```

属性 :

-   `session`: 代理实例
-   `enableAttachments`: 显示附件按钮( 默认: 真)
-   `enableModelSelector`: 显示模式选择器( 默认: 真)
-   `enableThinkingSelector`: 显示思维级别选择器( 默认: 真)
-   `showThemeToggle`: 显示主题切换( 默认: 假)

### (来自@draht/代理核心)

```typescript
import { Agent } from '@draht/agent-core';

const agent = new Agent({
  initialState: {
    model: getModel('anthropic', 'claude-sonnet-5'),
    systemPrompt: 'You are helpful.',
    thinkingLevel: 'off',
    messages: [],
    tools: [],
  },
  convertToLlm: defaultConvertToLlm,
});

// Events
agent.subscribe((event) => {
  switch (event.type) {
    case 'agent_start': // Agent loop started
    case 'agent_end':   // Agent loop finished
    case 'turn_start':  // LLM call started
    case 'turn_end':    // LLM call finished
    case 'message_start':
    case 'message_update': // Streaming update
    case 'message_end':
      break;
  }
});

// Send message
await agent.prompt('Hello!');
await agent.prompt({ role: 'user-with-attachments', content: 'Check this', attachments, timestamp: Date.now() });

// Control
agent.abort();
agent.state.model = newModel;
agent.state.thinkingLevel = 'medium';
agent.state.tools = [...];
agent.followUp(customMessage);
```

## 信件类型

### 用户信息

文件附件的用户信件 :

```typescript
const message: UserMessageWithAttachments = {
  role: 'user-with-attachments',
  content: 'Analyze this document',
  attachments: [pdfAttachment],
  timestamp: Date.now(),
};

// Type guard
if (isUserMessageWithAttachments(msg)) {
  console.log(msg.attachments);
}
```

### 人工行为

用于工艺品的持久性:

```typescript
const artifact: ArtifactMessage = {
  role: 'artifact',
  action: 'create', // or 'update', 'delete'
  filename: 'chart.html',
  content: '<div>...</div>',
  timestamp: new Date().toISOString(),
};

// Type guard
if (isArtifactMessage(msg)) {
  console.log(msg.filename);
}
```

### 自定义信件类型

通过声明合并扩展 :

```typescript
interface SystemNotification {
  role: 'system-notification';
  message: string;
  level: 'info' | 'warning' | 'error';
  timestamp: string;
}

declare module '@draht/agent-core' {
  interface CustomAgentMessages {
    'system-notification': SystemNotification;
  }
}

// Register renderer
registerMessageRenderer('system-notification', {
  render: (msg) => html`<div class="alert">${msg.message}</div>`,
});

// Extend convertToLlm
function myConvertToLlm(messages: AgentMessage[]): Message[] {
  const processed = messages.map((m) => {
    if (m.role === 'system-notification') {
      return { role: 'user', content: `<system>${m.message}</system>`, timestamp: Date.now() };
    }
    return m;
  });
  return defaultConvertToLlm(processed);
}
```

## 信件变形器

`convertToLlm` 将应用程序消息转换为 LLM 兼容格式 :

```typescript
import { defaultConvertToLlm, convertAttachments } from '@draht/web-ui';

// defaultConvertToLlm handles:
// - UserMessageWithAttachments → user message with image/text content blocks
// - ArtifactMessage → filtered out (UI-only)
// - Standard messages (user, assistant, toolResult) → passed through
```

## 工具

### Java脚本 REPL

在沙盒浏览器环境中执行 JavaScript :

```typescript
import { createJavaScriptReplTool } from '@draht/web-ui';

const replTool = createJavaScriptReplTool();

// Configure runtime providers for artifact/attachment access
replTool.runtimeProvidersFactory = () => [
  new AttachmentsRuntimeProvider(attachments),
  new ArtifactsRuntimeProvider(artifactsPanel, agent, true), // read-write
];

agent.state.tools = [replTool];
```

### 提取文档

在 URL 从文档中提取文本 :

```typescript
import { createExtractDocumentTool } from '@draht/web-ui';

const extractTool = createExtractDocumentTool();
extractTool.corsProxyUrl = 'https://corsproxy.io/?';

agent.state.tools = [extractTool];
```

### 艺术工具

被建入ArtifactsPanel,支持:HTML,SVG,Markdown,文本,JSON,图像,PDF,DOCX,XLSX.

```typescript
const artifactsPanel = new ArtifactsPanel();
artifactsPanel.agent = agent;

// The tool is available as artifactsPanel.tool
agent.state.tools = [artifactsPanel.tool];
```

### 自定义工具渲染器

```typescript
import { registerToolRenderer, type ToolRenderer } from '@draht/web-ui';

const myRenderer: ToolRenderer = {
  render(params, result, isStreaming) {
    return {
      content: html`<div>...</div>`,
      isCustom: false, // true = no card wrapper
    };
  },
};

registerToolRenderer('my_tool', myRenderer);
```

## 存储

### 设置

```typescript
import {
  AppStorage,
  IndexedDBStorageBackend,
  SettingsStore,
  ProviderKeysStore,
  SessionsStore,
  CustomProvidersStore,
  setAppStorage,
  getAppStorage,
} from '@draht/web-ui';

// Create stores
const settings = new SettingsStore();
const providerKeys = new ProviderKeysStore();
const sessions = new SessionsStore();
const customProviders = new CustomProvidersStore();

// Create backend with all store configs
const backend = new IndexedDBStorageBackend({
  dbName: 'my-app',
  version: 1,
  stores: [
    settings.getConfig(),
    providerKeys.getConfig(),
    sessions.getConfig(),
    SessionsStore.getMetadataConfig(),
    customProviders.getConfig(),
  ],
});

// Wire stores to backend
settings.setBackend(backend);
providerKeys.setBackend(backend);
sessions.setBackend(backend);
customProviders.setBackend(backend);

// Create and set global storage
const storage = new AppStorage(settings, providerKeys, sessions, customProviders, backend);
setAppStorage(storage);
```

### 设置

密钥值设置 :

```typescript
await storage.settings.set('proxy.enabled', true);
await storage.settings.set('proxy.url', 'https://proxy.example.com');
const enabled = await storage.settings.get<boolean>('proxy.enabled');
```

### 提供商KeysStore

API 按提供者键 :

```typescript
await storage.providerKeys.set('anthropic', 'sk-ant-...');
const key = await storage.providerKeys.get('anthropic');
const providers = await storage.providerKeys.list();
```

### 会话Store

用元数据聊天会话 :

```typescript
// Save session
await storage.sessions.save(sessionData, metadata);

// Load session
const data = await storage.sessions.get(sessionId);
const metadata = await storage.sessions.getMetadata(sessionId);

// List sessions (sorted by lastModified)
const allMetadata = await storage.sessions.getAllMetadata();

// Update title
await storage.sessions.updateTitle(sessionId, 'New Title');

// Delete
await storage.sessions.delete(sessionId);
```

### 自定义提供程序

自定义 LLM 提供者 :

```typescript
const provider: CustomProvider = {
  id: crypto.randomUUID(),
  name: 'My Ollama',
  type: 'ollama',
  baseUrl: 'http://localhost:11434',
};

await storage.customProviders.set(provider);
const all = await storage.customProviders.getAll();
```

## 附录

装入和处理文件 :

```typescript
import { loadAttachment, type Attachment } from '@draht/web-ui';

// From File input
const file = inputElement.files[0];
const attachment = await loadAttachment(file);

// From URL
const attachment = await loadAttachment('https://example.com/doc.pdf');

// From ArrayBuffer
const attachment = await loadAttachment(arrayBuffer, 'document.pdf');

// Attachment structure
interface Attachment {
  id: string;
  type: 'image' | 'document';
  fileName: string;
  mimeType: string;
  size: number;
  content: string;        // base64 encoded
  extractedText?: string; // For documents
  preview?: string;       // base64 preview image
}
```

所支持的格式:PDF,DOCX,XLSX,PPTX,图像,文本文件.

## CORS 代理服务器

对于具有 CORS 限制的浏览器环境:

```typescript
import { createStreamFn, shouldUseProxyForProvider, isCorsError } from '@draht/web-ui';

// AgentInterface auto-configures proxy from settings
// For manual setup:
agent.streamFn = createStreamFn(async () => {
  const enabled = await storage.settings.get<boolean>('proxy.enabled');
  return enabled ? await storage.settings.get<string>('proxy.url') : undefined;
});

// Providers requiring proxy:
// - zai: always
// - anthropic: only OAuth tokens (sk-ant-oat-*)
```

## 对话框

### 设置对话框

```typescript
import { SettingsDialog, ProvidersModelsTab, ProxyTab, ApiKeysTab } from '@draht/web-ui';

SettingsDialog.open([
  new ProvidersModelsTab(), // Custom providers + model list
  new ProxyTab(),           // CORS proxy settings
  new ApiKeysTab(),         // API keys per provider
]);
```

### 会话列表

```typescript
import { SessionListDialog } from '@draht/web-ui';

SessionListDialog.open(
  async (sessionId) => { /* load session */ },
  (deletedId) => { /* handle deletion */ },
);
```

### ApiKeyPromptDialog 数据

```typescript
import { ApiKeyPromptDialog } from '@draht/web-ui';

const success = await ApiKeyPromptDialog.prompt('anthropic');
```

### 模型选择器

```typescript
import { ModelSelector } from '@draht/web-ui';

ModelSelector.open(currentModel, (selectedModel) => {
  agent.state.model = selectedModel;
});
```

## 样式

导入预建的 CSS :

```typescript
import '@draht/web-ui/app.css';
```

或使用自定义配置的 Tailwind:

```css
@import '@mariozechner/mini-lit/themes/claude.css';
@tailwind base;
@tailwind components;
@tailwind utilities;
```

## 国际化

```typescript
import { i18n, setLanguage, translations } from '@draht/web-ui';

// Add translations
translations.de = {
  'Loading...': 'Laden...',
  'No sessions yet': 'Noch keine Sitzungen',
};

setLanguage('de');
console.log(i18n('Loading...')); // "Laden..."
```

## 实例

-   [实例/](https://npmcn.com/package/%40draht/web-ui/example) - 完成网络应用程序,包括会话、文物、自定义信息
-   [站点地质学](https://sitegeist.ai/) - 使用@draht/web-ui浏览器扩展

## 已知问题

-   **常态成像仪**: 已断开

## 许可证

麻省理工学院

## 常见问题

### @draht/web-ui 是什么？

由 @draht/ai提供动力的 AI 聊天界面可重复使用的 Web UI 组件

### 如何安装 @draht/web-ui？

在项目目录运行 npm install @draht/web-ui，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：lit（^3\.3\.1）、@mariozechner/mini-lit（^0\.2\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @draht/web-ui 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @draht/web-ui 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @draht/web-ui 可以商用吗？

@draht/web-ui 使用 MIT 许可。宽松许可：允许商业使用、修改和再分发，通常只需在分发时保留原版权与许可声明。以上为概括，不构成法律意见，请以许可原文为准。

### @draht/web-ui 的下载量有多少？

根据 npm 官方下载统计，@draht/web-ui 在 2026/10/13 – 2026/11/11 这 30 天内被下载 2,189 次，日均约 73 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入，适合横向比较热度，不能直接等同于使用人数。

### @draht/web-ui 还在维护吗？

最新版本 2026\.11\.5 发布于 2026/11/06（7 天前）；至今共发布 50 个版本，其中过去 12 个月发布了 50 个；首次发布于 2026/03/03。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

