ai-chat-ui-kit 0.1.0

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 (95) hide show
  1. package/.eslintrc.cjs +74 -0
  2. package/.github/actions/screenshot/action.yml +35 -0
  3. package/.github/workflows/pages.yml +46 -0
  4. package/README.md +285 -0
  5. package/docs/README.md +176 -0
  6. package/docs/api/components.md +344 -0
  7. package/docs/api/core.md +349 -0
  8. package/docs/chat-style-1-minimal.html +78 -0
  9. package/docs/chat-style-2-neon.html +74 -0
  10. package/docs/chat-style-3-glass.html +73 -0
  11. package/docs/chat-style-4-terminal.html +84 -0
  12. package/docs/chat-style-5-gradient.html +69 -0
  13. package/docs/chat-style-6-corporate.html +116 -0
  14. package/docs/examples/basic-chat.md +291 -0
  15. package/docs/examples/custom-plugins.md +431 -0
  16. package/docs/examples/multi-model.md +466 -0
  17. package/docs/guide/api-adapters.md +431 -0
  18. package/docs/guide/getting-started.md +244 -0
  19. package/docs/guide/headless-mode.md +508 -0
  20. package/docs/guide/plugins.md +416 -0
  21. package/docs/guide/themes.md +327 -0
  22. package/docs/index.html +256 -0
  23. package/docs/theme-preview-1-minimal.html +74 -0
  24. package/docs/theme-preview-2-neon.html +73 -0
  25. package/docs/theme-preview-3-glass.html +77 -0
  26. package/docs/theme-preview-4-terminal.html +86 -0
  27. package/docs/theme-preview-5-gradient.html +79 -0
  28. package/docs/theme-preview-6-corporate.html +71 -0
  29. package/examples/index.html +414 -0
  30. package/examples/react-app/App.tsx +131 -0
  31. package/examples/react-app/index.html +12 -0
  32. package/examples/react-app/main.tsx +15 -0
  33. package/examples/react-app/package.json +24 -0
  34. package/examples/vue-app/index.html +12 -0
  35. package/examples/vue-app/package.json +22 -0
  36. package/examples/vue-app/src/App.vue +145 -0
  37. package/examples/vue-app/src/main.ts +9 -0
  38. package/package.json +44 -0
  39. package/packages/components/package.json +25 -0
  40. package/packages/components/src/chat/chat.css +80 -0
  41. package/packages/components/src/chat/chat.ts +236 -0
  42. package/packages/components/src/index.ts +36 -0
  43. package/packages/components/src/input/input.css +52 -0
  44. package/packages/components/src/input/input.ts +116 -0
  45. package/packages/components/src/markdown/markdown.css +118 -0
  46. package/packages/components/src/markdown/markdown.ts +229 -0
  47. package/packages/components/src/message/message.css +56 -0
  48. package/packages/components/src/message/message.ts +72 -0
  49. package/packages/components/src/styles/global.css +43 -0
  50. package/packages/components/src/tool-call/tool-call.css +98 -0
  51. package/packages/components/src/tool-call/tool-call.ts +171 -0
  52. package/packages/components/src/types.ts +55 -0
  53. package/packages/components/src/utils/helpers.ts +128 -0
  54. package/packages/components/tsconfig.json +25 -0
  55. package/packages/components/tsup.config.ts +18 -0
  56. package/packages/core/package.json +47 -0
  57. package/packages/core/pnpm-lock.yaml +2032 -0
  58. package/packages/core/pnpm-workspace.yaml +2 -0
  59. package/packages/core/src/api/adapters.ts +717 -0
  60. package/packages/core/src/api/base.ts +210 -0
  61. package/packages/core/src/api/index.ts +54 -0
  62. package/packages/core/src/index.ts +93 -0
  63. package/packages/core/src/parser/latex.ts +274 -0
  64. package/packages/core/src/parser/markdown.test.ts +58 -0
  65. package/packages/core/src/parser/markdown.ts +206 -0
  66. package/packages/core/src/parser/mermaid.ts +276 -0
  67. package/packages/core/src/plugins/PluginManager.ts +232 -0
  68. package/packages/core/src/plugins/builtin.ts +406 -0
  69. package/packages/core/src/store/ChatStore.ts +163 -0
  70. package/packages/core/src/store/ModelConfigStore.ts +136 -0
  71. package/packages/core/src/store/ToolCallStore.ts +164 -0
  72. package/packages/core/src/store/base.ts +75 -0
  73. package/packages/core/src/types/index.ts +133 -0
  74. package/packages/core/tsup.config.ts +18 -0
  75. package/packages/themes/package.json +33 -0
  76. package/packages/themes/src/corporate/index.ts +52 -0
  77. package/packages/themes/src/corporate/theme.css +228 -0
  78. package/packages/themes/src/glass/index.ts +52 -0
  79. package/packages/themes/src/glass/theme.css +237 -0
  80. package/packages/themes/src/gradient/index.ts +53 -0
  81. package/packages/themes/src/gradient/theme.css +218 -0
  82. package/packages/themes/src/index.ts +13 -0
  83. package/packages/themes/src/minimal/index.ts +52 -0
  84. package/packages/themes/src/minimal/theme.css +198 -0
  85. package/packages/themes/src/neon/index.ts +52 -0
  86. package/packages/themes/src/neon/theme.css +233 -0
  87. package/packages/themes/src/terminal/index.ts +52 -0
  88. package/packages/themes/src/terminal/theme.css +235 -0
  89. package/packages/themes/src/types.ts +10 -0
  90. package/packages/themes/src/vite-env.d.ts +9 -0
  91. package/packages/themes/tsup.config.ts +21 -0
  92. package/pnpm-workspace.yaml +4 -0
  93. package/tsconfig.json +27 -0
  94. package/vite.config.ts +25 -0
  95. package/vitest.config.ts +28 -0
@@ -0,0 +1,416 @@
1
+ # 插件开发
2
+
3
+ 插件系统允许您扩展 AI Chat UI Kit 的功能,如消息预处理、消息后处理、UI 扩展等。
4
+
5
+ ## 插件接口
6
+
7
+ ### 插件基本结构
8
+
9
+ ```typescript
10
+ interface ChatPlugin {
11
+ // 插件名称(必填)
12
+ name: string;
13
+
14
+ // 插件初始化(可选)
15
+ init?: (context: PluginContext) => void | Promise<void>;
16
+
17
+ // 消息发送前(可选)
18
+ beforeSend?: (message: string, context: PluginContext) => string | Promise<string>;
19
+
20
+ // 消息发送后(可选)
21
+ afterSend?: (message: string, context: PluginContext) => void | Promise<void>;
22
+
23
+ // 接收到 AI 回复后(可选)
24
+ afterReceive?: (message: string, context: PluginContext) => string | Promise<string>;
25
+
26
+ // UI 扩展(可选)
27
+ renderUI?: (container: HTMLElement, context: PluginContext) => void;
28
+
29
+ // 销毁插件(可选)
30
+ destroy?: () => void | Promise<void>;
31
+ }
32
+
33
+ interface PluginContext {
34
+ // 获取当前消息列表
35
+ getMessages(): Message[];
36
+
37
+ // 添加消息
38
+ addMessage(message: Message): void;
39
+
40
+ // 获取配置
41
+ getConfig(): Record<string, any>;
42
+
43
+ // 更新配置
44
+ setConfig(config: Record<string, any>): void;
45
+
46
+ // 触发事件
47
+ emit(event: string, data?: any): void;
48
+
49
+ // 监听事件
50
+ on(event: string, handler: (data: any) => void): void;
51
+ }
52
+ ```
53
+
54
+ ## 创建插件
55
+
56
+ ### 示例 1:消息日志插件
57
+
58
+ 记录所有发送和接收的消息。
59
+
60
+ ```typescript
61
+ // plugins/message-logger.ts
62
+ import { ChatPlugin, PluginContext, Message } from '@ai-chat/core';
63
+
64
+ export class MessageLoggerPlugin implements ChatPlugin {
65
+ name = 'message-logger';
66
+
67
+ private context!: PluginContext;
68
+ private logs: Array<{ type: string; message: string; timestamp: number }> = [];
69
+
70
+ init(context: PluginContext) {
71
+ this.context = context;
72
+ console.log('[MessageLogger] 插件已初始化');
73
+ }
74
+
75
+ beforeSend(message: string, context: PluginContext): string {
76
+ this.logs.push({
77
+ type: 'send',
78
+ message,
79
+ timestamp: Date.now(),
80
+ });
81
+ console.log(`[MessageLogger] 发送消息: ${message}`);
82
+ return message; // 返回(可能修改后的)消息
83
+ }
84
+
85
+ afterReceive(message: string, context: PluginContext): string {
86
+ this.logs.push({
87
+ type: 'receive',
88
+ message,
89
+ timestamp: Date.now(),
90
+ });
91
+ console.log(`[MessageLogger] 接收消息: ${message}`);
92
+ return message; // 返回(可能修改后的)消息
93
+ }
94
+
95
+ // 获取日志
96
+ getLogs() {
97
+ return [...this.logs];
98
+ }
99
+
100
+ // 导出日志
101
+ exportLogs() {
102
+ const text = this.logs
103
+ .map(log => `${new Date(log.timestamp).toISOString()} [${log.type}] ${log.message}`)
104
+ .join('\n');
105
+ const blob = new Blob([text], { type: 'text/plain' });
106
+ const url = URL.createObjectURL(blob);
107
+ const a = document.createElement('a');
108
+ a.href = url;
109
+ a.download = 'chat-logs.txt';
110
+ a.click();
111
+ URL.revokeObjectURL(url);
112
+ }
113
+
114
+ destroy() {
115
+ console.log('[MessageLogger] 插件已销毁');
116
+ this.logs = [];
117
+ }
118
+ }
119
+ ```
120
+
121
+ ### 示例 2:敏感词过滤插件
122
+
123
+ 过滤消息中的敏感词。
124
+
125
+ ```typescript
126
+ // plugins/sensitive-word-filter.ts
127
+ import { ChatPlugin } from '@ai-chat/core';
128
+
129
+ interface SensitiveWordFilterOptions {
130
+ words: string[]; // 敏感词列表
131
+ replacement?: string; // 替换字符,默认 ***
132
+ }
133
+
134
+ export class SensitiveWordFilterPlugin implements ChatPlugin {
135
+ name = 'sensitive-word-filter';
136
+
137
+ private words: string[];
138
+ private replacement: string;
139
+
140
+ constructor(options: SensitiveWordFilterOptions) {
141
+ this.words = options.words;
142
+ this.replacement = options.replacement || '***';
143
+ }
144
+
145
+ beforeSend(message: string): string {
146
+ let filtered = message;
147
+ for (const word of this.words) {
148
+ const regex = new RegExp(word, 'gi');
149
+ filtered = filtered.replace(regex, this.replacement);
150
+ }
151
+ return filtered;
152
+ }
153
+ }
154
+ ```
155
+
156
+ ### 示例 3:Markdown 渲染插件
157
+
158
+ 将 AI 回复中的 Markdown 语法渲染为 HTML。
159
+
160
+ ```typescript
161
+ // plugins/markdown-renderer.ts
162
+ import { ChatPlugin } from '@ai-chat/core';
163
+ import { marked } from 'marked';
164
+ import DOMPurify from 'dompurify';
165
+
166
+ export class MarkdownRendererPlugin implements ChatPlugin {
167
+ name = 'markdown-renderer';
168
+
169
+ init(context) {
170
+ // 配置 marked
171
+ marked.setOptions({
172
+ breaks: true,
173
+ gfm: true,
174
+ });
175
+ }
176
+
177
+ afterReceive(message: string): string {
178
+ // 将 Markdown 转换为 HTML
179
+ const html = marked(message);
180
+ // sanitize HTML
181
+ const cleanHtml = DOMPurify.sanitize(html);
182
+ return cleanHtml;
183
+ }
184
+
185
+ // 自定义消息渲染
186
+ renderUI(container: HTMLElement, context: PluginContext) {
187
+ const messages = context.getMessages();
188
+ const lastMessage = messages[messages.length - 1];
189
+
190
+ if (lastMessage && lastMessage.role === 'assistant') {
191
+ const messageEl = container.querySelector('.ai-message__content');
192
+ if (messageEl) {
193
+ messageEl.innerHTML = lastMessage.content; // 已经包含 HTML
194
+ }
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ ### 示例 4:打字机效果插件
201
+
202
+ 让 AI 回复逐字显示,产生打字机效果。
203
+
204
+ ```typescript
205
+ // plugins/typewriter-effect.ts
206
+ import { ChatPlugin, PluginContext } from '@ai-chat/core';
207
+
208
+ interface TypewriterEffectOptions {
209
+ speed?: number; // 打字速度(毫秒/字符),默认 30ms
210
+ }
211
+
212
+ export class TypewriterEffectPlugin implements ChatPlugin {
213
+ name = 'typewriter-effect';
214
+
215
+ private speed: number;
216
+ private typingInterval: number | null = null;
217
+
218
+ constructor(options: TypewriterEffectOptions = {}) {
219
+ this.speed = options.speed || 30;
220
+ }
221
+
222
+ afterReceive(message: string, context: PluginContext): Promise<string> {
223
+ return new Promise((resolve) => {
224
+ let index = 0;
225
+ let displayedText = '';
226
+
227
+ // 先添加一个空消息
228
+ const tempMessage = { role: 'assistant' as const, content: '' };
229
+ context.addMessage(tempMessage);
230
+
231
+ // 逐字显示
232
+ this.typingInterval = window.setInterval(() => {
233
+ if (index < message.length) {
234
+ displayedText += message[index];
235
+ index++;
236
+
237
+ // 更新最后一条消息
238
+ const messages = context.getMessages();
239
+ messages[messages.length - 1] = {
240
+ ...messages[messages.length - 1],
241
+ content: displayedText,
242
+ };
243
+ // 触发更新
244
+ context.emit('message-updated', messages);
245
+ } else {
246
+ // 打字完成
247
+ if (this.typingInterval) {
248
+ clearInterval(this.typingInterval);
249
+ this.typingInterval = null;
250
+ }
251
+ resolve(message);
252
+ }
253
+ }, this.speed);
254
+ });
255
+ }
256
+
257
+ destroy() {
258
+ if (this.typingInterval) {
259
+ clearInterval(this.typingInterval);
260
+ }
261
+ }
262
+ }
263
+ ```
264
+
265
+ ## 使用插件
266
+
267
+ ### 注册插件
268
+
269
+ ```typescript
270
+ // main.ts / App.tsx
271
+ import { createChatStore } from '@ai-chat/core';
272
+ import { MessageLoggerPlugin } from './plugins/message-logger';
273
+ import { SensitiveWordFilterPlugin } from './plugins/sensitive-word-filter';
274
+
275
+ // 创建插件实例
276
+ const loggerPlugin = new MessageLoggerPlugin();
277
+ const filterPlugin = new SensitiveWordFilterPlugin({
278
+ words: ['敏感词1', '敏感词2'],
279
+ replacement: '***',
280
+ });
281
+
282
+ // 创建 store 时注册插件
283
+ const chatStore = createChatStore({
284
+ plugins: [loggerPlugin, filterPlugin],
285
+ // ...其他配置
286
+ });
287
+
288
+ // 或者在创建后注册
289
+ chatStore.registerPlugin(loggerPlugin);
290
+ ```
291
+
292
+ ### 插件执行顺序
293
+
294
+ 插件按照注册顺序依次执行:
295
+
296
+ ```typescript
297
+ const chatStore = createChatStore({
298
+ plugins: [
299
+ plugin1, // 先执行
300
+ plugin2, // 后执行
301
+ plugin3, // 最后执行
302
+ ],
303
+ });
304
+ ```
305
+
306
+ ### 禁用/启用插件
307
+
308
+ ```typescript
309
+ // 禁用插件
310
+ chatStore.disablePlugin('message-logger');
311
+
312
+ // 启用插件
313
+ chatStore.enablePlugin('message-logger');
314
+ ```
315
+
316
+ ## 高级用法
317
+
318
+ ### 插件间通信
319
+
320
+ ```typescript
321
+ // 插件 A
322
+ export class PluginA implements ChatPlugin {
323
+ name = 'plugin-a';
324
+
325
+ init(context: PluginContext) {
326
+ // 监听来自插件 B 的事件
327
+ context.on('plugin-b-event', (data) => {
328
+ console.log('收到插件 B 的消息:', data);
329
+ });
330
+ }
331
+ }
332
+
333
+ // 插件 B
334
+ export class PluginB implements ChatPlugin {
335
+ name = 'plugin-b';
336
+
337
+ afterSend(message: string, context: PluginContext): string {
338
+ // 向插件 A 发送事件
339
+ context.emit('plugin-b-event', { message });
340
+ return message;
341
+ }
342
+ }
343
+ ```
344
+
345
+ ### 插件配置
346
+
347
+ ```typescript
348
+ interface PluginWithConfig extends ChatPlugin {
349
+ init(context: PluginContext) {
350
+ const config = context.getConfig();
351
+
352
+ // 读取配置
353
+ const enabled = config['plugin-a']?.enabled ?? true;
354
+ const options = config['plugin-a']?.options ?? {};
355
+
356
+ if (enabled) {
357
+ // 应用配置
358
+ }
359
+ }
360
+ }
361
+
362
+ // 使用
363
+ const chatStore = createChatStore({
364
+ plugins: [new PluginWithConfig()],
365
+ config: {
366
+ 'plugin-a': {
367
+ enabled: true,
368
+ options: {
369
+ // 插件特定配置
370
+ },
371
+ },
372
+ },
373
+ });
374
+ ```
375
+
376
+ ## 内置插件
377
+
378
+ AI Chat UI Kit 提供了一些常用内置插件:
379
+
380
+ ### 1. 时间戳插件
381
+
382
+ ```typescript
383
+ import { TimestampPlugin } from '@ai-chat/plugins';
384
+
385
+ const timestampPlugin = new TimestampPlugin({
386
+ format: 'HH:mm', // 时间格式
387
+ position: 'right', // 显示位置:'left' | 'right' | 'center'
388
+ });
389
+ ```
390
+
391
+ ### 2. 消息去重插件
392
+
393
+ ```typescript
394
+ import { DeduplicationPlugin } from '@ai-chat/plugins';
395
+
396
+ const deduplicationPlugin = new DeduplicationPlugin({
397
+ timeWindow: 1000, // 去重时间窗口(毫秒)
398
+ });
399
+ ```
400
+
401
+ ### 3. 离线支持插件
402
+
403
+ ```typescript
404
+ import { OfflineSupportPlugin } from '@ai-chat/plugins';
405
+
406
+ const offlinePlugin = new OfflineSupportPlugin({
407
+ storageKey: 'chat-messages-offline',
408
+ syncOnReconnect: true,
409
+ });
410
+ ```
411
+
412
+ ## 下一步
413
+
414
+ - [API 适配器](./api-adapters.md) - 学习如何兼容不同后端 API
415
+ - [主题定制](./themes.md) - 学习如何自定义主题
416
+ - [API 参考](../../api/) - 查看完整 API 文档