This commit is contained in:
2026-04-25 16:36:34 +08:00
commit db90e7579b
1876 changed files with 189777 additions and 0 deletions

View File

@@ -0,0 +1,4 @@
{
"label": "API 接口",
"position": 99
}

View File

@@ -0,0 +1,249 @@
---
sidebar_position: 1
title: "@capital/common"
---
## 注册
### regGroupPanel
注册群组面板
```typescript
regGroupPanel({
name: `com.msgbyte.webview/grouppanel`,
label: '网页面板',
provider: PLUGIN_NAME,
extraFormMeta: [{ type: 'text', name: 'url', label: '网址' }],
render: GroupWebPanelRender,
});
```
参数类型: [PluginGroupPanel](#plugingrouppanel)
### regMessageInterpreter
注册消息解释器
```typescript
regMessageInterpreter({
name: '喵语翻译',
explainMessage(message: string) {
// 喵语 -> 人话
if (!isMiao(message)) {
return null;
}
return decode(message);
},
});
```
参数类型: [PluginMessageInterpreter](#pluginmessageinterpreter)
### regMessageRender
*注册多个仅生效最后一个*
注册消息渲染器, 输入消息文本返回渲染内容
```typescript
regMessageRender((message) => {
return <BBCode plainText={message} />;
});
```
### regChatInputAction
注册聊天输入框操作
```typescript
regChatInputAction({
label: '喵言喵语',
onClick: (actions) => {
openModal(createElement(SendMiaoModal, { actions }));
},
});
```
参数类型: [ChatInputAction](#chatinputaction)
### regPluginColorScheme
注册插件配色方案/主题
```typescript
regPluginColorScheme({
label: 'Miku 葱',
name: 'light+miku',
});
```
## 工具函数
### useGroupPanelParams
`hooks`中获取用户面板相关信息
```typescript
import { useGroupPanelParams } from '@capital/common';
const { groupId, panelId } = useGroupPanelParams();
```
### openModal
打开一个模态框
```typescript
openModal(
content: React.ReactNode,
props?: Pick<ModalProps, 'closable' | 'maskClosable'>
)
```
类型:
- [ModalProps](#modalprops)
### ModalWrapper
模态框包装器
```jsx
<ModalWrapper>
<div></div>
</ModalWrapper>
```
### useModalContext
获取模态框上下文
```typescript
const { closeModal } = useModalContext();
```
### getGlobalState
获取全局 `Redux` 状态上下文
```typescript
const state = getGlobalState();
```
### getCachedUserInfo
获取用户信息, 缓存版本
```typescript
const info = getCachedUserInfo(userId);
```
### getCachedConverseInfo
获取会话信息
```typescript
const info = getCachedConverseInfo(converseId);
```
## 类型
### PluginGroupPanel
```typescript
interface PluginGroupPanel {
/**
* 面板唯一标识
* @example com.msgbyte.webview/grouppanel
*/
name: string;
/**
* 面板显示名
*/
label: string;
/**
* 插件提供者, 用于引导没有安装插件的用户安装插件
*/
provider: string;
/**
* 额外的表单数据, 用于创建面板时使用
*/
extraFormMeta: FastFormFieldMeta[];
/**
* 该面板如何渲染
*/
render: React.ComponentType;
}
```
### PluginMessageInterpreter
插件消息解释器
```typescript
interface PluginMessageInterpreter {
name?: string;
explainMessage: (message: string) => React.ReactNode;
}
```
### ChatInputAction
消息输入框操作对象
```typescript
interface ChatInputAction {
label: string;
onClick: (actions: ChatInputActionContextProps) => void;
}
```
### GroupPanel
```typescript
interface GroupPanel {
id: string; // 在群组中唯一
name: string;
parentId?: string;
type: GroupPanelType;
provider?: string; // 面板提供者
pluginPanelName?: string; // 插件面板名
meta?: Record<string, unknown>;
}
```
### ModalProps
```typescript
interface ModalProps {
visible?: boolean;
onChangeVisible?: (visible: boolean) => void;
/**
* 是否显示右上角的关闭按钮
* @default false
*/
closable?: boolean;
/**
* 遮罩层是否可关闭
*/
maskClosable?: boolean;
}
```

View File

@@ -0,0 +1,22 @@
---
sidebar_position: 2
title: "@capital/component"
---
### Button
组件来自 [antd](https://ant.design/)
组件文档: [Button](https://ant.design/components/button-cn/)
### TextArea
组件来自 [antd](https://ant.design/)
组件文档: [TextArea](https://ant.design/components/input-cn/#components-input-demo-textarea)
### Image
组件来自 [antd](https://ant.design/)
组件文档: [Image](https://ant.design/components/image-cn/)

View File

@@ -0,0 +1,9 @@
---
sidebar_position: 3
title: 全局CSS变量
---
- `--tc-primary-color`: 主色调
- `--tc-background-image`: 背景图片
- `--tc-content-background-image`: 内容页背景图片
- `--tc-content-background-image-opacity`: 内容页背景图片透明度,默认 **0.15**

View File

@@ -0,0 +1,19 @@
---
sidebar_position: 4
title: "data-tc-role"
---
`Tailchat Role` 是一种通过`data-*`方式来标识DOM的一种方式表明该节点在`Tailchat`中的角色, 开发者可以通过这来找到对应角色的DOM节点
例如: `[data-tc-role=navbar]`
- `navbar`: 导航栏
- `navbar-personal`: 导航栏中的个人主页
- `navbar-groups`: 导航栏中的群组
- `navbar-settings`: 导航栏中的设置
- `sidebar-personal`: 个人主页中的侧边栏
- `sidebar-group`: 群组中的侧边栏
- `content-personal`: 个人主页中的内容
- `content-group`: 群组页面中的内容
- `modal`: 模态框
- `modal-mask`: 模态框的遮罩层

View File

@@ -0,0 +1,212 @@
---
sidebar_position: 3
title: 开发一个主题插件
---
`Tailchat` 中开发插件是非常方便的,我们从开发一个主题插件来开始
## 最终效果
![](/img/tutorial/plugin/2.png)
![](/img/tutorial/plugin/3.png)
![](/img/tutorial/plugin/4.png)
## 创建插件
> 如果是第一次开发需要确保已经初始化了插件开发环境。 [了解更多](./init-env.md)
```bash
tailchat create --template client-plugin
```
因为主题样式是一个纯前端的实现,因此我们选择`client-plugin`模板
通过交互式命令行提示完成创建工作
![](/img/tutorial/plugin/5.png)
此时的目录结构应该是这样的:
```
.
├── package-lock.json
├── package.json
├── node_modules
└── plugins
└── com.test.hutao
├── manifest.json
├── package.json
├── src
│   ├── index.tsx
│   └── translate.ts
├── tsconfig.json
└── types
└── tailchat.d.ts
```
这时候我们就可以立即开始编译插件了
```bash
npm run plugins:all
```
编译的产物会出现在 `/dist/plugins/com.test.hutao`。通过修改 `mini-star` 的配置可以让其输出在其他目录 *(这点会在服务端开发插件中会用到)*
## 在 Tailchat 中安装插件
对于前端插件我们需要把产物用一个http静态服务提供出来比如在本地开发中我们可以这样:
```bash
npx http-server .
```
![](/img/tutorial/plugin/6.png)
此时我们可以通过地址访问到我们编译出来的结果了,如 [http://127.0.0.1:8080/dist/plugins/com.test.hutao/index.js](http://127.0.0.1:8080/dist/plugins/com.test.hutao/index.js)
在 Tailchat 中提供了手动安装插件的方式,我们可以通过手动安装的方式安装插件。
复制插件目录下 `plugins/com.test.hutao/manifest.json` 的内容,粘贴到手动安装插件部分。
修改url为可以访问到的真实地址。点击确定按钮后自动执行安装命令:
![](/img/tutorial/plugin/7.png)
如果提示安装成功,打开控制台则可以看到对应输出:
![](/img/tutorial/plugin/8.png)
这里是插件里默认生成的逻辑,当日志输出了插件加载完毕的提示说明我们的插件已经被安装成功了。
## 编写样式文件并应用
因为我们需要修改主题样式,因此涉及到样式文件和静态资源的处理。接下来我们先来创建我们主题的样式
`plugins/com.test.hutao/src/theme.less`中写入样式:
```less
#tailchat-app.theme-genshin-hutao {
@primary-color: #dd5545;
--tc-primary-color: @primary-color;
--tc-background-image: url(./bg.jpg);
--tc-content-background-image: url(./avatar.png);
--tc-content-background-image-opacity: 0.15;
.bg-navbar-light {
background-color: @primary-color;
.bg-gray-400 {
background-color: darken(@primary-color, 10%);
}
}
.bg-sidebar-light {
background-color: lighten(@primary-color, 20%);
}
.bg-content-light {
background-color: lighten(@primary-color, 40%);
}
&.dark {
--tc-primary-color: darken(@primary-color, 10%);
.dark\:bg-navbar-dark {
background-color: darken(@primary-color, 40%);
}
.dark\:bg-sidebar-dark {
background-color: darken(@primary-color, 20%);
}
.dark\:bg-content-dark {
background-color: @primary-color;
}
}
}
```
这个样式文件做了三件事情:
- 通过 `less变量` 的方式指定主色调, 大幅度减少冗余代码
- 通过 `css变量` 的方式指定 `Tailchat` 的各处背景图, 主要是登录页的背景图与头像
- 在根节点使用`#tailchat-app.theme-genshin-hutao`选择器确保不会污染全局样式。
**对应的,需要在根节点下放入`bg.jpg`和`avatar.png`作为静态资源**
为了让 `Tailchat` 能够知道有这么一个主题存在,我们需要将这个主题相关的信息注册到 `Tailchat` 中.
```js
// plugins/com.test.hutao/src/index.tsx
import { regPluginColorScheme, sharedEvent } from '@capital/common';
regPluginColorScheme({
label: '原神-胡桃测试主题',
name: 'light+genshin-hutao',
});
sharedEvent.on('loadColorScheme', (colorSchemeName) => {
if (colorSchemeName === 'light+genshin-hutao') {
console.log('正在加载胡桃主题...');
import('./theme.less');
}
});
```
这个插件做了两件事情:
- 调用 `regPluginColorScheme` 将主题生命注册到 `Tailchat` 中,这样 `Tailchat` 就会在系统设置中显示该主题。
- 需要注意的是主题的 `name` 是以 `<color>+<theme-name>`的形式组成的,默认的 `Tailchat` 会提供 `auto`/`light`/`dark`三种配色方案,这里是指基于亮色模式追加样式覆盖的意思。
- 通过 `sharedEvent` 监听加载主题样式事件,如果配色主题是我们的主题,则异步加载主题(异步加载的目的是减少无意义的网络请求)
此时我们的编译是无法通过的因为对于less类型的文件还没有处理
`mini-star` 已经默认对less文件进行处理我们只需要安装一下 `less` 包以后 `mini-star` 会自动调用安装的less进行编译
```bash
npm install less
```
此时执行`npm run plugins:all`进行编译操作,最后目录应当如下:
```
.
├── dist
│   └── plugins
│   └── com.test.hutao
│   ├── index.js
│   ├── index.js.map
│   ├── theme-475203da.js
│   └── theme-475203da.js.map
├── package-lock.json
├── package.json
└── plugins
└── com.test.hutao
├── manifest.json
├── package.json
├── src
│   ├── avatar.png
│   ├── bg.jpg
│   ├── index.tsx
│   ├── theme.less
│   └── translate.ts
├── tsconfig.json
└── types
└── tailchat.d.ts
```
为了便于管理,`mini-star` 在处理less引用的静态资源时会直接以 `base64` 的格式把图片打包到样式文件中。因此主题文件会非常大,这也是为什么我们需要按需异步加载的原因
![](/img/tutorial/plugin/9.png)
因为我们之前已经在 Tailchat 中安装过我们开发中的插件了,因此直接刷新即可。
在左下角设置菜单中点开系统设置的主题页面,我们就可以看到我们的主题了。切换过去立即界面会变成我们想要的主题风格。
![](/img/tutorial/plugin/10.png)
## 源码参考
官方的该主题实现可以访问 [https://github.com/msgbyte/tailchat/tree/master/client/web/plugins/com.msgbyte.theme.genshin](https://github.com/msgbyte/tailchat/tree/master/client/web/plugins/com.msgbyte.theme.genshin) 查看

View File

@@ -0,0 +1,54 @@
---
sidebar_position: 90
title: 部署插件
---
:::info
插件的部署策略后续会进行优化目前tailchat对部署时自定义插件还做的不够便利因此可能会有些困惑。因此本文档仅代表现状后续会不断改进
:::
首先说一下 Tailchat 插件的分类Tailchat 的插件分为 `纯前端插件`, `纯后端插件`, `前后端插件`
`纯前端插件`最为容易理解表示插件仅运行在前端代码中依赖已有的上下文进行沟通无需Tailchat后端支持。特别的对于与自定义后端进行通信的也是前端插件`com.msgbyte.ai-assistant`插件)
`纯后端插件为`为与Tailchat网络进行通信的插件无前端界面通过rpc调用其他服务的action来实现一些目的。对于后端插件来说意味着插件本身接入了Tailchat后端网络拥有最高的权限可以访问一些可见性为`public`的action(默认只有`publish`级别的action可以被外部访问其他级别的都是只有内部服务才能访问),一个无前端的后端插件如`com.msgbyte.simplenotify`插件
`前后端插件` 表示既有前端又有后端的插件,是最复杂但是能力最完全的插件类型,通过前端插件与后端交互,通过后端插件与其他的服务进行交互,在不修改核心代码的前提下能完成大部分的开发工作
你可以在这个文档中查看对应的插件示例: [插件列表](/docs/plugin-list/fe)
## 部署流程
在不同的插件部署的流程是不一样的
### 纯前端插件
纯前端插件你可以将代码放置在项目内部`client/web/plugins`目录下或者使用单独的静态文件服务管理。确保项目的js文档都是能正常访问的。
如果这是一个个人使用的插件,你可以通过插件中心的手动安装来安装,输入`manifest.json`的json配置即可。注意确保配置文件中的地址都是可以正常访问的。
如果想让所有的用户都能够看见代码,你需要在`client/web/registry.json`中添加你自己的插件配置。
如果想要成为一个默认安装的内置插件,需要修改`client/web/src/plugin/builtin.ts`文件来让前端代码在启动时加载插件。
### 纯后端插件
为了让插件能够被自动加载,你可以将代码放置与 `server/plugins` 目录下或者独立部署,只需要确保能够连入同一个网络即可(共用同一个TRANSPORTER)
默认部署的插件服务会将该目录下的所有后端插件统一加载。需要确保插件服务的命名为`*.service.ts`/`*.service.js`
*如果仅想在开发环境运行而在生产环境忽略,请将文件命名为 `*.service.dev.ts`*
你可以访问后端 `/health`(如:`http://localhost:11000/health`) 路由或者使用`tailchat connect`工具查看已加载的微服务列表
### 前后端插件
前后端插件的部署与后端差不多,但是需要修改编译命令让构建时能够把前后端插件的前端代码部署到`public/plugins`目录下
具体方式是: 修改`package.json`, 在 `build:server` 命令中追加想要编译的插件名称
> 配置文件中的 {BACKEND} 指代后端地址,因为前后端分离的关系后端地址不一定和前端地址保持一致
--------
所有操作完毕后记得重新构建 docker 镜像

View File

@@ -0,0 +1,62 @@
---
sidebar_position: 1
title: 初始化插件开发环境
---
在开发一个插件之前,我们需要创建一个插件开发环境,这个环境可以是直接复用 Tailchat 官方源码的插件环境([https://github.com/msgbyte/tailchat/tree/master/client/web/plugins](https://github.com/msgbyte/tailchat/tree/master/client/web/plugins)),也可以是一个独立的项目
这里主要教大家怎么创建一个独立的插件开发环境
## 前端插件开发环境
创建一个插件非常简单, 在此之前如果我们没有初始化插件环境的话需要先初始化一下开发环境
我们先随便找个地方建一个项目文件夹:
```bash
mkdir tailchat-plugin-test && cd tailchat-plugin-test
```
在根目录下执行:
```bash
npm init -y
npm install mini-star
```
在根目录创建 `mini-star` 的配置文件 `.ministarrc.js`,内容如下:
```js
// .ministarrc.js
const path = require('path');
module.exports = {
externalDeps: [
'react',
'react-router',
'axios',
'styled-components',
'zustand',
'zustand/middleware/immer',
],
};
```
`package.json` 中写入编译脚本
```json
{
//...
"scripts": {
// ...
"plugins:all": "ministar buildPlugin all",
"plugins:watch": "ministar watchPlugin all",
// ...
}
//...
}
```
## 后端插件开发环境
TODO

View File

@@ -0,0 +1,51 @@
---
sidebar_position: 2
title: Typescript 类型支持
---
`Tailchat` 中拥有一些从核心项目共享出来的工具函数或者组件,你可以通过 `@capital/common``@capital/component` 来引用。
当然如果直接引用的话会有一些类型问题。因为此时typescript的类型系统是不知道能够引入什么以及有什么类型的。
在这里可能会有两种情况:
## 在Tailchat本体项目中进行开发
你可以通过`tsconfig.json``paths`字段来引入同一目录下的文件, 这样在解析时typescript可以直接加载完整的类型系统
如:
```json
{
"compilerOptions": {
"baseUrl": "./src",
"esModuleInterop": true,
"jsx": "react",
"paths": {
"@capital/*": ["../../../src/plugin/*"],
}
}
}
```
## 在独立项目中进行开发
你可以通过获取 Tailchat 预生成好的声明文件进行开发。
> 因为类型要手动重写因为有部分类型尚是any。但是能够保证开发者不会引入不存在的函数
如果你使用的是 `tailchat create` 命令创建的项目,命令行工具模板已为您添加了如下命令
```json
"scripts": {
"sync:declaration": "tailchat declaration github"
},
```
用法
```bash
pnpm sync:declaration
```
该命令会自动拉取远程的配置文件并写入当前目录下的 `types/tailchat.d.ts` 文件中。如果你是手动创建的项目,你可以将其添加到你的`package.json`中以方便后续使用

View File

@@ -0,0 +1,20 @@
---
sidebar_position: 10
title: Icon 图标
---
```ts
import { Icon } from '@capital/component';
```
`tailchat` 的 icon 解决方案来自 `iconify`
使用方法很简单:
- 在下述网站中选择想要的图标: [https://icon-sets.iconify.design/](https://icon-sets.iconify.design/)
- 复制选中的key。传给 `Icon` 组件, 示例:
```tsx
<Icon icon="mdi:account" />
```
推荐使用`mdi`来统一化图标视觉设计:
[https://icon-sets.iconify.design/mdi/](https://icon-sets.iconify.design/mdi/)

View File

@@ -0,0 +1,87 @@
---
sidebar_position: 1
title: 开始开发插件
---
## 认识 MiniStar
`MiniStar` 是一套完整的微内核架构开发工具链,`Tailchat`的插件架构就是基于 `MiniStar` 进行开发。
关于更多的 `MiniStar` 相关问题可以查看 `MiniStar` 的官方文档: [https://ministar.moonrailgun.com/](https://ministar.moonrailgun.com/)
## 创建一个基本项目
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';
<Tabs groupId="start">
<TabItem value="cli" label="使用命令行工具快速创建" default>
> 首先确保你已经安装了 `tailchat-cli`, 关于命令行工具可参照: [tailchat-cli](../cli/tailchat-cli)
移动到项目目录下(可以是自定义项目):
```bash
tailchat create
```
根据实际情况选择模板
![](/img/tutorial/plugin/1.png)
</TabItem>
<TabItem value="ministar" label="使用原生ministar创建插件项目" default>
创建一个基本的 npm 项目, 并全局安装 `MiniStar`
```bash
npm install --global mini-star
```
在项目中执行: `ministar createPlugin` 来创建一个基本的插件
在项目中执行: `ministar buildPlugin` 来编译插件
> 值得一提的是, 虽然 `Tailchat` 并没有强制规定插件命名规范,但是还是推荐使用 `反域名` 的命名方式(类似于java中的包命名), 然后对插件中的部件,使用 `/` 进行分割
>
> 如:
> 插件名: `com.msgbyte.webview`
>
> 注册内容: `com.msgbyte.webview/grouppanel`
</TabItem>
</Tabs>
## 安装插件
### 手动安装插件
在不经过任何预设的情况下,一个通用的办法是自己构造一个 `manifest` 配置, 然后在 `tailchat` 提供手动安装插件 Tab 中将配置文件粘贴进去安装。
插件的url路径可以通过 `oss对象存储服务` / `static-server` 等办法代理
一个作为示例的`manifest.json`配置如下:
```json
{
"label": "网页面板插件",
"name": "com.msgbyte.webview",
"url": "/plugins/com.msgbyte.webview/index.js",
"version": "0.0.0",
"author": "msgbyte",
"description": "为群组提供创建网页面板的功能",
"requireRestart": false
}
```
## 其他有用的资源
- 插件化架构内核依赖库 [MiniStar](https://ministar.moonrailgun.com/)
- 来自基础项目提供的API: [API 文档](./api/common)
- 导出接口源码
- [@capital/common](https://github.com/msgbyte/tailchat/blob/master/client/web/src/plugin/common/index.ts)
- [@capital/component](https://github.com/msgbyte/tailchat/blob/master/client/web/src/plugin/component/index.tsx)