@lobehub/chat 0.68.1 â 0.69.1
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.
- package/.eslintrc.js +1 -0
- package/.github/workflows/wiki-sync.yml +18 -0
- package/.i18nrc.js +1 -1
- package/CHANGELOG.md +50 -0
- package/README-zh_CN.md +44 -90
- package/README.md +30 -76
- package/docs/Home.md +26 -0
- package/docs/Plugin-Development.en-US.md +262 -0
- package/docs/Plugin-Development.zh-CN.md +261 -0
- package/docs/_Footer.md +1 -0
- package/docs/_Sidebar.md +16 -0
- package/package.json +3 -3
- package/src/components/EmojiPicker/style.ts +3 -1
- package/src/components/Empty/index.tsx +6 -3
- package/src/components/Empty/style.ts +3 -0
- package/src/features/AvatarWithUpload/index.tsx +3 -1
- package/src/features/PluginDevModal/index.tsx +1 -1
- package/src/pages/_document.page.tsx +5 -6
- package/src/pages/api/openai.ts +33 -36
- package/src/pages/chat/SessionList/Header.tsx +2 -0
- package/src/pages/chat/features/Conversation/ChatList/Plugins/IFrameRender/index.tsx +1 -1
- package/src/pages/chat/features/Conversation/ChatList/Plugins/IFrameRender/utils.ts +1 -1
- package/src/pages/chat/features/Conversation/ChatList/Plugins/SystemJsRender/index.tsx +1 -1
- package/src/pages/welcome/Footer/index.tsx +3 -1
- package/src/services/chatModel.ts +1 -2
- package/src/store/plugin/selectors.ts +1 -1
- package/src/types/openai.ts +23 -0
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
# LobeChat Plugin Development
|
|
2
|
+
|
|
3
|
+
#### TOC
|
|
4
|
+
|
|
5
|
+
- [Plugin Composition](#plugin-composition)
|
|
6
|
+
|
|
7
|
+
- [Custom Plugin Workflow](#custom-plugin-workflow)
|
|
8
|
+
|
|
9
|
+
- [1. Create and Start a Plugin Project](#1-create-and-start-a-plugin-project)
|
|
10
|
+
- [2. Add the Local Plugin in LobeChat Role Settings](#2-add-the-local-plugin-in-lobechat-role-settings)
|
|
11
|
+
- [3. Test the Plugin Functionality in a Session](#3-test-the-plugin-functionality-in-a-session)
|
|
12
|
+
|
|
13
|
+
- [Local Plugin Development](#local-plugin-development)
|
|
14
|
+
|
|
15
|
+
- [Manifest](#manifest)
|
|
16
|
+
- [Project Structure](#project-structure)
|
|
17
|
+
- [Server-side](#server-side)
|
|
18
|
+
- [Plugin UI Interface](#plugin-ui-interface)
|
|
19
|
+
|
|
20
|
+
- [Plugin Deployment and Publication](#plugin-deployment-and-publication)
|
|
21
|
+
|
|
22
|
+
## Plugin Composition
|
|
23
|
+
|
|
24
|
+
A LobeChat plugin consists of the following components:
|
|
25
|
+
|
|
26
|
+
1. **Plugin Index**: Used to display basic information about the plugin, including the plugin name, description, author, version, and a link to the plugin manifest. The official plugin index can be found at [lobe-chat-plugins](https://github.com/lobehub/lobe-chat-plugins). To submit a plugin to the official plugin marketplace, you need to submit a PR to this repository.
|
|
27
|
+
2. **Plugin Manifest**: Used to describe the functionality of the plugin, including the server-side description, frontend display information, and version number. For more details about the manifest, please refer to the [manifest][manifest-docs-url].
|
|
28
|
+
3. **Plugin Services**: Used to implement the server-side and frontend modules described in the manifest:
|
|
29
|
+
|
|
30
|
+
- **Server-side**: Implement the API capabilities described in the manifest.
|
|
31
|
+
- **Frontend UI** (optional): Implement the interface described in the manifest, which will be displayed in plugin messages to provide richer information display than plain text.
|
|
32
|
+
|
|
33
|
+
## Custom Plugin Workflow
|
|
34
|
+
|
|
35
|
+
To integrate a plugin into LobeChat, you need to add and use a custom plugin in LobeChat. This section will guide you through the process.
|
|
36
|
+
|
|
37
|
+
### 1. Create and Start a Plugin Project
|
|
38
|
+
|
|
39
|
+
First, you need to create a plugin project locally. You can use the [lobe-chat-plugin-template][lobe-chat-plugin-template-url] template we have prepared:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
$ git clone https://github.com/lobehub/chat-plugin-template.git
|
|
43
|
+
$ cd chat-plugin-template
|
|
44
|
+
$ npm i
|
|
45
|
+
$ npm run dev
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
When you see `ready started server on 0.0.0.0:3400, url: http://localhost:3400`, it means that the plugin service has been successfully started locally.
|
|
49
|
+
|
|
50
|
+

|
|
51
|
+
|
|
52
|
+
### 2. Add the Local Plugin in LobeChat Role Settings
|
|
53
|
+
|
|
54
|
+
Next, go to LobeChat, create a new assistant, and go to its session settings page:
|
|
55
|
+
|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
Click the "Add" button on the right side of "Plugin List" to open the custom plugin add dialog:
|
|
59
|
+
|
|
60
|
+

|
|
61
|
+
|
|
62
|
+
Enter `http://localhost:3400/manifest-dev.json` in the "Plugin Manifest URL" field, which is the URL of the locally started plugin manifest.
|
|
63
|
+
|
|
64
|
+
At this point, you should see that the identifier of the plugin has been automatically recognized as `chat-plugin-template`. Then fill in the remaining form fields (only the title is required) and click the "Save" button to complete the custom plugin addition.
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
After adding the plugin, you can see the newly added plugin in the plugin list. If you need to modify the plugin's configuration, you can click the "Settings" button to make changes.
|
|
69
|
+
|
|
70
|
+

|
|
71
|
+
|
|
72
|
+
### 3. Test the Plugin Functionality in a Session
|
|
73
|
+
|
|
74
|
+
Next, we need to test the functionality of the custom plugin.
|
|
75
|
+
|
|
76
|
+
Click the "Back" button to go back to the session area, and then send a message to the assistant: "What should I wear?" The assistant will try to ask you about your gender and current mood.
|
|
77
|
+
|
|
78
|
+

|
|
79
|
+
|
|
80
|
+
After answering, the assistant will make a plugin call to retrieve recommended clothing data based on your gender and mood from the server and push it to you. Finally, it will summarize the information in a text response.
|
|
81
|
+
|
|
82
|
+

|
|
83
|
+
|
|
84
|
+
After completing these steps, you have learned the basic process of adding and using a custom plugin in LobeChat.
|
|
85
|
+
|
|
86
|
+
## Local Plugin Development
|
|
87
|
+
|
|
88
|
+
In the previous workflow, we have learned how to add and use a plugin. Now let's focus on the development process of custom plugins.
|
|
89
|
+
|
|
90
|
+
### Manifest
|
|
91
|
+
|
|
92
|
+
The manifest aggregates information about how the plugin's functionality is implemented. The core fields are `api` and `ui`, which describe the server-side API capabilities and the frontend rendering interface address of the plugin, respectively.
|
|
93
|
+
|
|
94
|
+
Taking the manifest in our template as an example:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"api": [
|
|
99
|
+
{
|
|
100
|
+
"url": "http://localhost:3400/api/clothes",
|
|
101
|
+
"name": "recommendClothes",
|
|
102
|
+
"description": "Recommend clothes based on the user's mood",
|
|
103
|
+
"parameters": {
|
|
104
|
+
"properties": {
|
|
105
|
+
"mood": {
|
|
106
|
+
"description": "The user's current mood, with optional values: happy, sad, anger, fear, surprise, disgust",
|
|
107
|
+
"enums": ["happy", "sad", "anger", "fear", "surprise", "disgust"],
|
|
108
|
+
"type": "string"
|
|
109
|
+
},
|
|
110
|
+
"gender": {
|
|
111
|
+
"type": "string",
|
|
112
|
+
"enum": ["man", "woman"],
|
|
113
|
+
"description": "The gender of the user, which needs to be asked before knowing this information"
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
"required": ["mood", "gender"],
|
|
117
|
+
"type": "object"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
],
|
|
121
|
+
"gateway": "http://localhost:3400/api/gateway",
|
|
122
|
+
"identifier": "chat-plugin-template",
|
|
123
|
+
"ui": {
|
|
124
|
+
"url": "http://localhost:3400",
|
|
125
|
+
"height": 200
|
|
126
|
+
},
|
|
127
|
+
"version": "1"
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
In this manifest, the following parts are included:
|
|
132
|
+
|
|
133
|
+
1. `identifier`: This is the unique identifier of the plugin, used to distinguish different plugins. This field needs to be globally unique.
|
|
134
|
+
2. `api`: This is an array that contains all the API interface information provided by the plugin. Each interface includes the `url`, `name`, `description`, and `parameters` fields, all of which are required. The `description` and `parameters` fields will be sent to GPT as the `functions` parameter of the [Function Call](https://sspai.com/post/81986). The parameters need to comply with the [JSON Schema](https://json-schema.org/) specification. In this example, the API interface is named `recommendClothes`, which recommends clothes based on the user's mood and gender. The parameters of the interface include the user's mood and gender, both of which are required.
|
|
135
|
+
3. `ui`: This field contains information about the plugin's user interface, indicating where LobeChat loads the frontend interface of the plugin from. Since the plugin interface loading in LobeChat is implemented based on `iframe`, you can specify the height and width of the plugin interface as needed.
|
|
136
|
+
4. `gateway`: This field specifies the gateway for LobeChat to query API interfaces. The default plugin gateway in LobeChat is a cloud service, but for custom plugins, the requests need to be sent to the local service. Therefore, by specifying the gateway in the manifest, LobeChat will directly request this address and access the local plugin service. The gateway field does not need to be specified for plugins published online.
|
|
137
|
+
5. `version`: This is the version number of the plugin, which is currently not used.
|
|
138
|
+
|
|
139
|
+
In actual development, you can modify the plugin's manifest according to your needs to declare the functionality you want to implement. For a complete introduction to each field in the manifest, please refer to: [manifest][manifest-docs-url].
|
|
140
|
+
|
|
141
|
+
### Project Structure
|
|
142
|
+
|
|
143
|
+
The [lobe-chat-plugin-template][lobe-chat-plugin-template-url] template project uses Next.js as the development framework. Its core directory structure is as follows:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
â chat-plugin-template
|
|
147
|
+
âââ public
|
|
148
|
+
â âââ manifest-dev.json # Manifest file
|
|
149
|
+
âââ src
|
|
150
|
+
â âââ pages
|
|
151
|
+
â â âââ api # Next.js server-side folder
|
|
152
|
+
â â â âââ clothes.ts # Implementation of the recommendClothes interface
|
|
153
|
+
â â â âââ gateway.ts # Local plugin proxy gateway
|
|
154
|
+
â â âââ index.tsx # Frontend display interface
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Of course, using Next.js as the development framework in the template is just because we are familiar with Next.js and it is convenient for development. You can use any frontend framework and programming language you are familiar with as long as it can implement the functionality described in the manifest.
|
|
158
|
+
|
|
159
|
+
We also welcome contributions of plugin templates in more frameworks and languages.
|
|
160
|
+
|
|
161
|
+
### Server-side
|
|
162
|
+
|
|
163
|
+
The server-side only needs to implement the API interfaces described in the manifest. In the template, we use Vercel's [Edge Runtime](https://nextjs.org/docs/pages/api-reference/edge) as the server, which eliminates the need for operational maintenance.
|
|
164
|
+
|
|
165
|
+
#### API Implementation
|
|
166
|
+
|
|
167
|
+
For Edge Runtime, we provide the `createErrorResponse` method in `@lobehub/chat-plugin-sdk` to quickly return error responses. The currently provided error types can be found at: [PluginErrorType][plugin-error-type-url].
|
|
168
|
+
|
|
169
|
+
Here is an example of the clothes API implementation in the template:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
export default async (req: Request) => {
|
|
173
|
+
if (req.method !== 'POST') return createErrorResponse(PluginErrorType.MethodNotAllowed);
|
|
174
|
+
|
|
175
|
+
const { gender, mood } = (await req.json()) as RequestData;
|
|
176
|
+
|
|
177
|
+
const clothes = gender === 'man' ? manClothes : womanClothes;
|
|
178
|
+
|
|
179
|
+
const result: ResponseData = {
|
|
180
|
+
clothes: clothes[mood] || [],
|
|
181
|
+
mood,
|
|
182
|
+
today: Date.now(),
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
return new Response(JSON.stringify(result));
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
In this example, `manClothes` and `womanClothes` are hardcoded mock data. In actual scenarios, they can be replaced with database queries.
|
|
190
|
+
|
|
191
|
+
#### Gateway
|
|
192
|
+
|
|
193
|
+
Since the default plugin gateway in LobeChat is a cloud service (<https://chat.lobehub.com/api/plugins>), which sends requests to the API addresses specified in the manifest to solve the cross-origin issue.
|
|
194
|
+
|
|
195
|
+
For custom plugins, the requests need to be sent to the local service. Therefore, by specifying the gateway in the manifest (<http://localhost:3400/api/gateway>), LobeChat will directly request this address. Then you only need to create a gateway implementation at this address.
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
import { createLobeChatPluginGateway } from '@lobehub/chat-plugins-gateway';
|
|
199
|
+
|
|
200
|
+
export const config = {
|
|
201
|
+
runtime: 'edge',
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export default async createLobeChatPluginGateway();
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
[`@lobehub/chat-plugins-gateway`](https://github.com/lobehub/chat-plugins-gateway) includes the implementation of the plugin gateway in LobeChat, which you can use to create a gateway. This allows LobeChat to access the local plugin service.
|
|
208
|
+
|
|
209
|
+
### Plugin UI Interface
|
|
210
|
+
|
|
211
|
+
For a plugin, the UI interface is optional. For example, the [Web Crawler](https://github.com/lobehub/chat-plugin-web-crawler) plugin does not provide a corresponding user interface.
|
|
212
|
+
|
|
213
|
+

|
|
214
|
+
|
|
215
|
+
If you want to display richer information in plugin messages or include some rich interactions, you can define a user interface for the plugin. For example, the following image shows the user interface of a search engine plugin.
|
|
216
|
+
|
|
217
|
+

|
|
218
|
+
|
|
219
|
+
#### Plugin UI Interface Implementation
|
|
220
|
+
|
|
221
|
+
LobeChat uses `iframe` + `postMessage` to load and communicate with plugin UI. Therefore, the implementation of the plugin UI is the same as normal web development. You can use any frontend framework and programming language you are familiar with.
|
|
222
|
+
|
|
223
|
+

|
|
224
|
+
|
|
225
|
+
In our template, we use React + Next.js + antd as the frontend framework. You can find the implementation of the user interface in `src/pages/index.tsx`.
|
|
226
|
+
|
|
227
|
+
Regarding plugin communication, we provide related methods in [`@lobehub/chat-plugin-sdk`](https://github.com/lobehub/chat-plugin-sdk) to simplify the communication between the plugin and LobeChat. You can use the `fetchPluginMessage` method to actively retrieve the data of the current message from LobeChat. For a detailed description of this method, please refer to: [fetchPluginMessage][fetch-plugin-message-url].
|
|
228
|
+
|
|
229
|
+
```tsx
|
|
230
|
+
import { fetchPluginMessage } from '@lobehub/chat-plugin-sdk';
|
|
231
|
+
import { memo, useEffect, useState } from 'react';
|
|
232
|
+
|
|
233
|
+
import { ResponseData } from '@/type';
|
|
234
|
+
|
|
235
|
+
const Render = memo(() => {
|
|
236
|
+
const [data, setData] = useState<ResponseData>();
|
|
237
|
+
|
|
238
|
+
useEffect(() => {
|
|
239
|
+
// Retrieve the current plugin message from LobeChat
|
|
240
|
+
fetchPluginMessage().then((e: ResponseData) => {
|
|
241
|
+
setData(e);
|
|
242
|
+
});
|
|
243
|
+
}, []);
|
|
244
|
+
|
|
245
|
+
return <>...</>;
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
export default Render;
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Plugin Deployment and Publication
|
|
252
|
+
|
|
253
|
+
After completing the plugin development, you can deploy the plugin using your preferred method. For example, you can use Vercel or package it as a Docker image for publication.
|
|
254
|
+
|
|
255
|
+
If you want more people to use your plugin, you are welcome to submit it for review in the plugin marketplace.
|
|
256
|
+
|
|
257
|
+
<!-- LINK GROUP -->
|
|
258
|
+
|
|
259
|
+
[fetch-plugin-message-url]: https://github.com/lobehub/chat-plugin-template
|
|
260
|
+
[lobe-chat-plugin-template-url]: https://github.com/lobehub/chat-plugin-template
|
|
261
|
+
[manifest-docs-url]: https://github.com/lobehub/chat-plugin-sdk/tree/master/docs/manifest.zh-CN.md
|
|
262
|
+
[plugin-error-type-url]: https://github.com/lobehub/chat-plugin-template
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
# LobeChat æäģļåŧå
|
|
2
|
+
|
|
3
|
+
#### TOC
|
|
4
|
+
|
|
5
|
+
- [æäģļææ](#æäģļææ)
|
|
6
|
+
|
|
7
|
+
- [čĒåŽäšæäģļæĩį¨](#čĒåŽäšæäģļæĩį¨)
|
|
8
|
+
|
|
9
|
+
- [1. ååģēåšļå¯å¨æäģļ饚įŽ](#1-ååģēåšļå¯å¨æäģļ饚įŽ)
|
|
10
|
+
- [2. å¨ LobeChat č§č˛čŽžįŊŽä¸æˇģå æŦå°æäģļ](#2-å¨-lobechat-č§č˛čŽžįŊŽä¸æˇģå æŦå°æäģļ)
|
|
11
|
+
- [3. äŧ蝿ĩ蝿äģļåčŊ](#3-äŧ蝿ĩ蝿äģļåčŊ)
|
|
12
|
+
|
|
13
|
+
- [æŦå°æäģļåŧå](#æŦå°æäģļåŧå)
|
|
14
|
+
|
|
15
|
+
- [manifest](#manifest)
|
|
16
|
+
- [饚įŽįģæ](#饚įŽįģæ)
|
|
17
|
+
- [æåĄį̝](#æåĄį̝)
|
|
18
|
+
- [æäģļ UI įéĸ](#æäģļ-ui-įéĸ)
|
|
19
|
+
|
|
20
|
+
- [æäģļé¨įŊ˛ä¸åå¸](#æäģļé¨įŊ˛ä¸åå¸)
|
|
21
|
+
|
|
22
|
+
## æäģļææ
|
|
23
|
+
|
|
24
|
+
ä¸ä¸Ē LobeChat įæäģļįąäģĨä¸å ä¸Ēé¨åįģæīŧ
|
|
25
|
+
|
|
26
|
+
1. **æäģļį´ĸåŧ**īŧį¨äēåąį¤ēæäģļįåēæŦäŋĄæ¯īŧå
æŦæäģļåį§°ãæčŋ°ãäŊč
ãįæŦãæäģļæčŋ°æ¸
åįéžæĨīŧåŽæšįæäģļį´ĸåŧå°åīŧ[lobe-chat-plugins](https://github.com/lobehub/lobe-chat-plugins)ãčĨæŗä¸æļæäģļå°åŽæšæäģļå¸åēīŧéčĻ[æäē¤ PR](https://github.com/lobehub/lobe-chat-plugins/pulls) å°č¯Ĩäģåēīŧ
|
|
27
|
+
2. **æäģļæčŋ°æ¸
å (manifest)**īŧį¨äēæčŋ°æäģļįåčŊåŽį°īŧå
åĢäēæäģļįæåĄį̝æčŋ°ãåį̝åąį¤ēäŋĄæ¯ãįæŦåˇįãå
ŗäē manifest įč¯Ļįģäģįģīŧč¯Ļč§ [manifest][manifest-docs-url]īŧ
|
|
28
|
+
3. **æäģļæåĄ**īŧį¨äēåŽį°æäģļæčŋ°æ¸
å䏿æčŋ°įæåĄį̝ååįĢ¯æ¨ĄåīŧååĢåĻä¸īŧ
|
|
29
|
+
- **æåĄį̝**īŧéčĻåŽį° manifest 䏿čŋ°į `api` é¨åįæĨåŖčŊåīŧ
|
|
30
|
+
- **åį̝ UI**īŧå¯éīŧīŧéčĻåŽį° manifest 䏿čŋ°į `ui` é¨åįįéĸīŧč¯Ĩįéĸå°äŧ卿äģļæļæ¯ä¸éåēīŧčŋčåŽį°æ¯ææŦæ´å 丰å¯å°äŋĄæ¯åąį¤ēæšåŧã
|
|
31
|
+
|
|
32
|
+
## čĒåŽäšæäģļæĩį¨
|
|
33
|
+
|
|
34
|
+
æäģļåŧåéčĻå°æäģļéæčŋ LobeChat ä¸īŧå æ¤æŦé¨åå°äŧäģįģåĻäŊå¨ LobeChat 䏿ˇģå åäŊŋį¨ä¸ä¸ĒčĒåŽäšæäģļã
|
|
35
|
+
|
|
36
|
+
### 1. ååģēåšļå¯å¨æäģļ饚įŽ
|
|
37
|
+
|
|
38
|
+
äŊ éčĻå
卿Ŧå°ååģēä¸ä¸Ēæäģļ饚įŽīŧå¯äģĨäŊŋ፿äģŦåå¤åĨŊį [lobe-chat-plugin-template][lobe-chat-plugin-template-url] æ¨Ąæŋīŧ
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
$ git clone https://github.com/lobehub/chat-plugin-template.git
|
|
42
|
+
$ cd chat-plugin-template
|
|
43
|
+
$ npm i
|
|
44
|
+
$ npm run dev
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
åŊåēį° `ready started server on 0.0.0.0:3400, url: http://localhost:3400` æļīŧ蝴ææäģļæåĄåˇ˛įģ卿Ŧå°å¯å¨æåã
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
### 2. å¨ LobeChat č§č˛čŽžįŊŽä¸æˇģå æŦå°æäģļ
|
|
52
|
+
|
|
53
|
+
æĨ䏿Ĩčŋå
Ĩå° LobeChat ä¸īŧååģēä¸ä¸Ēæ°įåŠæīŧåšļčŋå
ĨåŽįäŧč¯čŽžįŊŽéĄĩīŧ
|
|
54
|
+
|
|
55
|
+

|
|
56
|
+
|
|
57
|
+
įšåģãæäģļå襨ãåŗäž§įãæˇģå ãæéŽīŧæåŧčĒåŽäšæäģᅫģå åŧšįĒīŧ
|
|
58
|
+
|
|
59
|
+

|
|
60
|
+
|
|
61
|
+
å¨ ãæäģļæčŋ°æäģļ Url å°åãä¸åĄĢå
Ĩ `http://localhost:3400/manifest-dev.json` īŧčŋæ¯æäģŦæŦå°å¯å¨įæäģļæčŋ°æ¸
åå°åã
|
|
62
|
+
|
|
63
|
+
æ¤æļīŧäŊ åēč¯Ĩå¯äģĨįå°įå°æäģļįæ č¯įŦĻ䏿 厞įģčĸĢčĒå¨č¯åĢä¸ē `chat-plugin-template`ãæĨ䏿ĨäŊ éčĻåĄĢååŠä¸į襨ååæŽĩīŧåĒææ éĸåŋ
åĄĢīŧīŧįļåįšåģãäŋåãæéŽīŧåŗå¯åŽæčĒåŽäšæäģᅫģå ã
|
|
64
|
+
|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
åŽææˇģå åīŧåŗå¯å¨æäģļå襨ä¸įå°å忎ģå įæäģļīŧåĻæéčĻäŋŽæšæäģļįé
įŊŽīŧå¯äģĨįšåģã莞įŊŽãæéŽčŋčĄäŋŽæšã
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
### 3. äŧ蝿ĩ蝿äģļåčŊ
|
|
72
|
+
|
|
73
|
+
æĨæĨ䏿äģŦéčĻæĩč¯ä¸čŋä¸ĒčĒåŽäšæäģļįåčŊæ¯åĻæŖå¸¸ã
|
|
74
|
+
|
|
75
|
+
įšåģãčŋåãæéŽåå°äŧč¯åēīŧįļåååŠæåéæļæ¯īŧãæåēč¯ĨįŠŋäģäšīŧ ãæ¤æļåŠæå°äŧå°č¯åäŊ č¯ĸéŽīŧäēč§ŖäŊ įæ§åĢä¸åŊåįåŋæ
ã
|
|
76
|
+
|
|
77
|
+

|
|
78
|
+
|
|
79
|
+
åŊåįåŽæ¯åīŧåŠæå°äŧåčĩˇæäģļįč°į¨īŧæ šæŽäŊ įæ§åĢãåŋæ
īŧäģæåĄį̝čˇåæ¨čįčĄŖææ°æŽīŧåšļæ¨éįģäŊ ãæååēäēčŋäēäŋĄæ¯åä¸čŊŽææŦæģįģã
|
|
80
|
+
|
|
81
|
+

|
|
82
|
+
|
|
83
|
+
åŊåŽæčŋäēæäŊåīŧäŊ 厞įģäēč§Ŗä翎ģå čĒåŽäšæäģļīŧåšļå¨ LobeChat ä¸äŊŋį¨įåēįĄæĩį¨ã
|
|
84
|
+
|
|
85
|
+
## æŦå°æäģļåŧå
|
|
86
|
+
|
|
87
|
+
å¨ä¸čŋ°æĩį¨ä¸īŧæäģŦ厞įģäēč§Ŗæäģļ῎ģå åäŊŋį¨įæšåŧīŧæĨ䏿ĨéįšäģįģčĒåŽäšæäģļåŧåįčŋį¨ã
|
|
88
|
+
|
|
89
|
+
### manifest
|
|
90
|
+
|
|
91
|
+
manifest čåäēæäģļåčŊåĻäŊåŽį°įäŋĄæ¯ãæ ¸åŋįåæŽĩä¸ē `api` ä¸ `ui`īŧååĢæčŋ°äēæäģļįæåĄį̝æĨåŖčŊåä¸åįĢ¯æ¸˛æįįéĸå°åã
|
|
92
|
+
|
|
93
|
+
äģĨæäģŦæäžįæ¨Ąæŋä¸į manifest ä¸ēäžīŧ
|
|
94
|
+
|
|
95
|
+
```json
|
|
96
|
+
{
|
|
97
|
+
"api": [
|
|
98
|
+
{
|
|
99
|
+
"url": "http://localhost:3400/api/clothes",
|
|
100
|
+
"name": "recommendClothes",
|
|
101
|
+
"description": "æ šæŽį¨æˇįåŋæ
īŧįģ፿ˇæ¨čäģæįčĄŖæ",
|
|
102
|
+
"parameters": {
|
|
103
|
+
"properties": {
|
|
104
|
+
"mood": {
|
|
105
|
+
"description": "፿ˇåŊåįåŋæ
īŧå¯éåŧæīŧåŧåŋīŧhappyīŧ, éžčŋīŧsadīŧ,įæ° īŧangerīŧ,åŽŗæīŧfearīŧ,æåīŧ surpriseīŧ,åæļ īŧdisgustīŧ",
|
|
106
|
+
"enums": ["happy", "sad", "anger", "fear", "surprise", "disgust"],
|
|
107
|
+
"type": "string"
|
|
108
|
+
},
|
|
109
|
+
"gender": {
|
|
110
|
+
"type": "string",
|
|
111
|
+
"enum": ["man", "woman"],
|
|
112
|
+
"description": "寚č¯į¨æˇįæ§åĢīŧéčĻč¯ĸéŽį¨æˇåæįĨéčŋä¸ĒäŋĄæ¯"
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"required": ["mood", "gender"],
|
|
116
|
+
"type": "object"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
"gateway": "http://localhost:3400/api/gateway",
|
|
121
|
+
"identifier": "chat-plugin-template",
|
|
122
|
+
"ui": {
|
|
123
|
+
"url": "http://localhost:3400",
|
|
124
|
+
"height": 200
|
|
125
|
+
},
|
|
126
|
+
"version": "1"
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
å¨čŋäģŊ manifest ä¸īŧä¸ģčĻå
åĢäēäģĨä¸å ä¸Ēé¨åīŧ
|
|
131
|
+
|
|
132
|
+
1. `identifier`īŧčŋæ¯æäģļįå¯ä¸æ č¯įŦĻīŧ፿Ĩåēåä¸åįæäģļīŧčŋä¸ĒåæŽĩéčĻå
¨åąå¯ä¸ã
|
|
133
|
+
2. `api`īŧčŋæ¯ä¸ä¸Ēæ°įģīŧå
åĢäēæäģļææäžįææ API æĨåŖäŋĄæ¯ãæ¯ä¸ĒæĨåŖéŊå
åĢäēurlãnameãdescriptionå parameters åæŽĩīŧåä¸ēåŋ
åĄĢ饚ãå
ļä¸ `description` å `parameters` 两ä¸ĒåæŽĩīŧå°äŧäŊä¸ē [Function Call](https://sspai.com/post/81986) į `functions` åæ°åéįģ gptīŧ parameters éčĻįŦĻå [JSON Schema](https://json-schema.org/) č§čã å¨čŋä¸Ēäžåä¸īŧapi æĨåŖåä¸ē `recommendClothes` īŧčŋä¸ĒæĨåŖįåčŊæ¯æ šæŽį¨æˇįåŋæ
åæ§åĢæĨæ¨ččĄŖæãæĨåŖįåæ°å
æŦ፿ˇįåŋæ
åæ§åĢīŧčŋ两ä¸Ēåæ°éŊæ¯åŋ
åĄĢ饚ã
|
|
134
|
+
3. `ui`īŧčŋä¸ĒåæŽĩå
åĢäēæäģļį፿ˇįéĸäŋĄæ¯īŧææäē LobeChat äģåĒä¸Ēå°åå čŊŊæäģļįåį̝įéĸãįąäē LobeChat æäģļįéĸå čŊŊæ¯åēäē iframe åŽį°įīŧå æ¤å¯äģĨæéæåŽæäģļįéĸįéĢåēĻãåŽŊåēĻã
|
|
135
|
+
4. `gateway`īŧčŋä¸ĒåæŽĩæåŽäē LobeChat æĨč¯ĸ api æĨåŖįįŊå
ŗãLobeChat éģ莤įæäģļįŊå
ŗæ¯äēį̝æåĄīŧččĒåŽäšæäģļį蝎æąéčĻåéįģæŦå°æåĄįīŧå æ¤éčŋå¨ manifest 䏿åŽįŊå
ŗīŧLobeChat å°äŧį´æĨ蝎æąčŋä¸Ēå°åīŧčŋččŽŋéŽå°æŦå°įæäģļæåĄīŧåå¸å°įēŋä¸įæäģļå¯äģĨä¸į¨æåŽč¯ĨåæŽĩã
|
|
136
|
+
5. `version`īŧčŋæ¯æäģļįįæŦåˇīŧį°éļæŽĩææļæ˛ĄæäŊį¨īŧ
|
|
137
|
+
|
|
138
|
+
å¨åŽé
åŧåä¸īŧäŊ å¯äģĨæ šæŽčĒåˇąįéæąīŧäŋŽæšæäģļįæčŋ°æ¸
åīŧåŖ°æčĒåˇąæŗčĻåŽį°įåčŊã å
ŗäē manifest åä¸ĒåæŽĩįåŽæ´äģįģīŧåč§īŧ[manifest][manifest-docs-url]ã
|
|
139
|
+
|
|
140
|
+
### 饚įŽįģæ
|
|
141
|
+
|
|
142
|
+
[lobe-chat-plugin-template][lobe-chat-plugin-template-url] čŋä¸Ēæ¨Ąæŋ饚įŽäŊŋį¨äē Next.js äŊä¸ēåŧåæĄæļīŧåŽįæ ¸åŋįŽåŊįģæåĻä¸īŧ
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
â chat-plugin-template
|
|
146
|
+
âââ public
|
|
147
|
+
â âââ manifest-dev.json # æčŋ°æ¸
åæäģļ
|
|
148
|
+
âââ src
|
|
149
|
+
â âââ pages
|
|
150
|
+
â â âââ api # nextjs æåĄį̝æäģļ多
|
|
151
|
+
â â â âââ clothes.ts # recommendClothes æĨåŖåŽį°
|
|
152
|
+
â â â âââ gateway.ts # æŦå°æäģļäģŖįįŊå
ŗ
|
|
153
|
+
â â âââ index.tsx # åį̝åąį¤ēįéĸ
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
åŊįļīŧæ¨ĄæŋäŊŋ፠Next.js äŊä¸ēåŧåæĄæļīŧåĒæ¯å ä¸ēæäģŦįæ Next.jsīŧåŧåæšäžŋãäŊ å¯äģĨäŊŋį¨äģģäŊäŊ įæįåŧåæĄæļä¸åŧåč¯č¨īŧåĒčĻčŊå¤åŽį° manifest 䏿čŋ°įåčŊåŗå¯ã
|
|
157
|
+
|
|
158
|
+
åæļäšæŦĸčŋč´ĄįŽæ´å¤æĄæļä¸č¯č¨įæäģᅪĄæŋã
|
|
159
|
+
|
|
160
|
+
### æåĄį̝
|
|
161
|
+
|
|
162
|
+
æåĄį̝åĒéčĻåŽį° manifest 䏿čŋ°į api æĨåŖåŗå¯ã卿¨Ąæŋä¸īŧæäģŦäŊŋį¨äē vercel į [Edge Runtime](https://nextjs.org/docs/pages/api-reference/edge) äŊä¸ēæåĄīŧå
åģčŋįģ´ææŦã
|
|
163
|
+
|
|
164
|
+
#### api åŽį°
|
|
165
|
+
|
|
166
|
+
é寚 Edge Runtime īŧæäģŦå¨ `@lobehub/chat-plugin-sdk` æäžäē `createErrorResponse` æšæŗīŧį¨äēåŋĢéčŋåé蝝ååēãįŽåæäžįé蝝įąģåč¯Ļč§īŧ[PluginErrorType][plugin-error-type-url]ã
|
|
167
|
+
|
|
168
|
+
æ¨Ąæŋä¸į clothes æĨåŖåŽį°åĻä¸īŧ
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
export default async (req: Request) => {
|
|
172
|
+
if (req.method !== 'POST') return createErrorResponse(PluginErrorType.MethodNotAllowed);
|
|
173
|
+
|
|
174
|
+
const { gender, mood } = (await req.json()) as RequestData;
|
|
175
|
+
|
|
176
|
+
const clothes = gender === 'man' ? manClothes : womanClothes;
|
|
177
|
+
|
|
178
|
+
const result: ResponseData = {
|
|
179
|
+
clothes: clothes[mood] || [],
|
|
180
|
+
mood,
|
|
181
|
+
today: Date.now(),
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
return new Response(JSON.stringify(result));
|
|
185
|
+
};
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
å
ļä¸ `maniClothes` å `womanClothes` æ¯åæģį mock æ°æŽīŧå¨åŽé
å翝ä¸īŧå¯äģĨæŋæĸä¸ēæ°æŽåē蝎æąã
|
|
189
|
+
|
|
190
|
+
#### gateway
|
|
191
|
+
|
|
192
|
+
įąäē LobeChat éģ莤įæäģļįŊå
ŗæ¯äēį̝æåĄīŧ<https://chat.lobehub.com/api/plugins>īŧīŧäēį̝æåĄéčŋ manifest ä¸į api å°ååé蝎æąīŧäģĨč§Ŗåŗčˇ¨åéŽéĸã
|
|
193
|
+
|
|
194
|
+
čé寚čĒåŽäšæäģļīŧæäģļį蝎æąéčĻåéįģæŦå°æåĄįīŧ å æ¤éčŋå¨ manifest 䏿åŽįŊå
ŗ (<http://localhost:3400/api/gateway>)īŧLobeChat å°äŧį´æĨ蝎æąč¯Ĩå°åīŧįļååĒéčĻå¨č¯Ĩå°åä¸ååģēä¸ä¸ĒįŊå
ŗåŽį°åŗå¯ã
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
import { createLobeChatPluginGateway } from '@lobehub/chat-plugins-gateway';
|
|
198
|
+
|
|
199
|
+
export const config = {
|
|
200
|
+
runtime: 'edge',
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
export default async createLobeChatPluginGateway();
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
[`@lobehub/chat-plugins-gateway`](https://github.com/lobehub/chat-plugins-gateway) å
åĢäē LobeChat 䏿äģļįŊå
ŗį[åŽį°](https://github.com/lobehub/lobe-chat/blob/main/src/pages/api/plugins.api.ts)īŧäŊ å¯äģĨį´æĨäŊŋį¨č¯Ĩå
ååģēįŊå
ŗīŧčŋč莊 LobeChat čŽŋéŽå°æŦå°įæäģļæåĄã
|
|
207
|
+
|
|
208
|
+
### æäģļ UI įéĸ
|
|
209
|
+
|
|
210
|
+
寚äēä¸ä¸Ēæäģļčč¨īŧUI įéĸæ¯ä¸ä¸Ēå¯é饚ãäžåĻ [ãįŊéĄĩå
厚æåãæäģļ](https://github.com/lobehub/chat-plugin-web-crawler)īŧå°ąæ˛ĄæåŽį°į¸åēį፿ˇįéĸã
|
|
211
|
+
|
|
212
|
+

|
|
213
|
+
|
|
214
|
+
åĻæäŊ 叿卿äģļæļæ¯ä¸åąį¤ēæ´å 丰å¯įäŋĄæ¯īŧæč
å
åĢä¸äēå¯äē¤äēīŧå¯äģĨä¸ēæäģļåŽäšä¸ä¸Ē፿ˇįéĸãäžåĻä¸åžīŧåä¸ēæį´ĸåŧææäģļį፿ˇįéĸã
|
|
215
|
+
|
|
216
|
+

|
|
217
|
+
|
|
218
|
+
#### æäģļ UI įéĸåŽį°
|
|
219
|
+
|
|
220
|
+
LobeChat éčŋ `iframe` + `postMessage` åŽį°æäģļ ui įå čŊŊä¸éäŋĄãå æ¤īŧ æäģļ UI įåŽį°æšåŧ䏿ŽéįįŊéĄĩåŧåä¸č´īŧäŊ å¯äģĨäŊŋį¨äģģäŊäŊ įæįåįĢ¯æĄæļä¸åŧåč¯č¨ã
|
|
221
|
+
|
|
222
|
+

|
|
223
|
+
|
|
224
|
+
卿äģŦæäžįæ¨Ąæŋä¸īŧæäģŦäŊŋį¨äē React + Next.js + antd äŊä¸ēåį̝įéĸæĄæļīŧäŊ å¯äģĨå¨ `src/pages/index.tsx` 䏿žå°į¨æˇįéĸįåŽį°ã
|
|
225
|
+
|
|
226
|
+
å
ļä¸å
ŗäēæäģļéäŋĄīŧæäģŦå¨ [`@lobehub/chat-plugin-sdk`](https://github.com/lobehub/chat-plugin-sdk) æäžäēį¸å
ŗæšæŗīŧį¨äēįŽåæäģļä¸ LobeChat įéäŋĄãäŊ å¯äģĨéčŋ `fetchPluginMessage` æšæŗä¸ģå¨å LobeChat čˇååŊåæļæ¯įæ°æŽãå
ŗäēč¯Ĩæšæŗįč¯Ļįģäģįģīŧåč§īŧ[fetchPluginMessage][fetch-plugin-message-url]ã
|
|
227
|
+
|
|
228
|
+
```tsx
|
|
229
|
+
import { fetchPluginMessage } from '@lobehub/chat-plugin-sdk';
|
|
230
|
+
import { memo, useEffect, useState } from 'react';
|
|
231
|
+
|
|
232
|
+
import { ResponseData } from '@/type';
|
|
233
|
+
|
|
234
|
+
const Render = memo(() => {
|
|
235
|
+
const [data, setData] = useState<ResponseData>();
|
|
236
|
+
|
|
237
|
+
useEffect(() => {
|
|
238
|
+
// äģ LobeChat čˇååŊåæäģļįæļæ¯
|
|
239
|
+
fetchPluginMessage().then((e: ResponseData) => {
|
|
240
|
+
setData(e);
|
|
241
|
+
});
|
|
242
|
+
}, []);
|
|
243
|
+
|
|
244
|
+
return <>...</>;
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
export default Render;
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## æäģļé¨įŊ˛ä¸åå¸
|
|
251
|
+
|
|
252
|
+
åŊäŊ åŽææäģļįåŧååīŧäŊ å¯äģĨäŊŋį¨äŊ äš æ¯įæšåŧčŋčĄæäģļįé¨įŊ˛ãäžåĻäŊŋ፠vercel īŧæč
æå
æ docker åå¸įįã
|
|
253
|
+
|
|
254
|
+
åĻæäŊ 叿æäģļčĸĢæ´å¤äēēäŊŋį¨īŧæŦĸčŋå°äŊ įæäģļ [æäē¤ä¸æļ](https://github.com/lobehub/lobe-chat-plugins) å°æäģļå¸åēã
|
|
255
|
+
|
|
256
|
+
<!-- LINK GROUP -->
|
|
257
|
+
|
|
258
|
+
[fetch-plugin-message-url]: https://github.com/lobehub/chat-plugin-template
|
|
259
|
+
[lobe-chat-plugin-template-url]: https://github.com/lobehub/chat-plugin-template
|
|
260
|
+
[manifest-docs-url]: https://github.com/lobehub/chat-plugin-sdk/tree/master/docs/manifest.zh-CN.md
|
|
261
|
+
[plugin-error-type-url]: https://github.com/lobehub/chat-plugin-template
|
package/docs/_Footer.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
This is the **đ¤¯ / đ¤ Lobe Chat** wiki. [Wiki Home](https://github.com/lobehub/lobe-chat/wiki)
|
package/docs/_Sidebar.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## Lobe Chat Wiki
|
|
2
|
+
|
|
3
|
+
### đ Home
|
|
4
|
+
|
|
5
|
+
- [TOC](Home)
|
|
6
|
+
|
|
7
|
+
### đ¤ Agents
|
|
8
|
+
|
|
9
|
+
- [Agent Index](https://github.com/lobehub/lobe-chat-agents)
|
|
10
|
+
|
|
11
|
+
### đ§Š Plugins
|
|
12
|
+
|
|
13
|
+
- [Plugin Index](https://github.com/lobehub/lobe-chat-plugins)
|
|
14
|
+
- [Plugin Development](Plugin-Development.en-US)
|
|
15
|
+
- [English](Plugin-Development.en-US)
|
|
16
|
+
- [įŽäŊ䏿](Plugin-Development.zh-CN)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobehub/chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.69.1",
|
|
4
4
|
"description": "Lobe Chat is an open-source chatbot client using LangChain, Typescript and Next.js",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"chatbot",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
"@emoji-mart/data": "^1",
|
|
67
67
|
"@emoji-mart/react": "^1",
|
|
68
68
|
"@icons-pack/react-simple-icons": "^9",
|
|
69
|
-
"@lobehub/chat-plugin-sdk": "^1.
|
|
69
|
+
"@lobehub/chat-plugin-sdk": "^1.17.0",
|
|
70
70
|
"@lobehub/chat-plugins-gateway": "^1.5.0",
|
|
71
71
|
"@lobehub/ui": "latest",
|
|
72
72
|
"@vercel/analytics": "^1",
|
|
@@ -85,7 +85,7 @@
|
|
|
85
85
|
"lucide-react": "latest",
|
|
86
86
|
"nanoid": "^4",
|
|
87
87
|
"next": "13.4.7",
|
|
88
|
-
"openai
|
|
88
|
+
"openai": "^4.4.0",
|
|
89
89
|
"polished": "^4",
|
|
90
90
|
"react": "^18",
|
|
91
91
|
"react-dom": "^18",
|
|
@@ -4,7 +4,9 @@ import chroma from 'chroma-js';
|
|
|
4
4
|
export const useStyles = createStyles(({ css, token, prefixCls }) => ({
|
|
5
5
|
avatar: css`
|
|
6
6
|
border-radius: 50%;
|
|
7
|
-
transition:
|
|
7
|
+
transition:
|
|
8
|
+
scale 400ms ${token.motionEaseOut},
|
|
9
|
+
box-shadow 100ms ${token.motionEaseOut};
|
|
8
10
|
|
|
9
11
|
&:hover {
|
|
10
12
|
box-shadow: 0 0 0 3px ${token.colorText};
|
|
@@ -2,6 +2,7 @@ import { ActionIcon, DivProps } from '@lobehub/ui';
|
|
|
2
2
|
import { X } from 'lucide-react';
|
|
3
3
|
import Image from 'next/image';
|
|
4
4
|
import { memo } from 'react';
|
|
5
|
+
import { Flexbox } from 'react-layout-kit';
|
|
5
6
|
import useMergeState from 'use-merge-value';
|
|
6
7
|
|
|
7
8
|
import { useStyles } from './style';
|
|
@@ -40,19 +41,21 @@ const Empty = memo<EmptyProps>(
|
|
|
40
41
|
const { styles } = useStyles();
|
|
41
42
|
if (!value) return null;
|
|
42
43
|
return (
|
|
43
|
-
<
|
|
44
|
+
<Flexbox className={styles.container} {...props}>
|
|
44
45
|
<ActionIcon
|
|
45
46
|
className={styles.close}
|
|
46
47
|
icon={X}
|
|
47
48
|
onClick={() => setValue(false)}
|
|
48
49
|
size={{ blockSize: 24, fontSize: 16 }}
|
|
49
50
|
/>
|
|
50
|
-
{cover &&
|
|
51
|
+
{cover && (
|
|
52
|
+
<Image alt={alt} className={styles.image} height={height} src={cover} width={width} />
|
|
53
|
+
)}
|
|
51
54
|
<div className={styles.content}>
|
|
52
55
|
{title && <h3>{title}</h3>}
|
|
53
56
|
{desc && <p className={styles.desc}>{desc}</p>}
|
|
54
57
|
</div>
|
|
55
|
-
</
|
|
58
|
+
</Flexbox>
|
|
56
59
|
);
|
|
57
60
|
},
|
|
58
61
|
);
|
|
@@ -10,7 +10,9 @@ const useStyle = createStyles(
|
|
|
10
10
|
({ css, token }) => css`
|
|
11
11
|
cursor: pointer;
|
|
12
12
|
border-radius: 50%;
|
|
13
|
-
transition:
|
|
13
|
+
transition:
|
|
14
|
+
scale 400ms ${token.motionEaseOut},
|
|
15
|
+
box-shadow 100ms ${token.motionEaseOut};
|
|
14
16
|
|
|
15
17
|
&:hover {
|
|
16
18
|
box-shadow: 0 0 0 3px ${token.colorText};
|
|
@@ -5,12 +5,11 @@ import Document, { DocumentContext, Head, Html, Main, NextScript } from 'next/do
|
|
|
5
5
|
class MyDocument extends Document {
|
|
6
6
|
static async getInitialProps(ctx: DocumentContext) {
|
|
7
7
|
const page = await ctx.renderPage({
|
|
8
|
-
enhanceApp: (App) => (props) =>
|
|
9
|
-
|
|
10
|
-
<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
),
|
|
8
|
+
enhanceApp: (App) => (props) => (
|
|
9
|
+
<StyleProvider cache={extractStaticStyle.cache}>
|
|
10
|
+
<App {...props} />
|
|
11
|
+
</StyleProvider>
|
|
12
|
+
),
|
|
14
13
|
});
|
|
15
14
|
|
|
16
15
|
const styles = extractStaticStyle(page.html).map((item) => item.style);
|