@fe-free/ai 4.1.9 → 4.1.11
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/CHANGELOG.md +19 -0
- package/package.json +4 -4
- package/src/ai.stories.tsx +103 -57
- package/src/helper.tsx +11 -2
- package/src/index.ts +1 -1
- package/src/m_sender/actions.tsx +22 -12
- package/src/m_sender/index.tsx +7 -15
- package/src/m_sender/m_sender.stories.tsx +16 -5
- package/src/m_sender/record.tsx +8 -43
- package/src/m_sender/types.ts +2 -1
- package/src/messages/index.tsx +3 -68
- package/src/messages/message_actions.tsx +85 -0
- package/src/messages/messages.tsx +92 -0
- package/src/store/index.ts +20 -2
- package/src/store/types.ts +15 -2
- package/src/style.scss +4 -4
- package/src/svgs/keyboard.svg +3 -1
- package/src/svgs/record.svg +3 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @fe-free/ai
|
|
2
2
|
|
|
3
|
+
## 4.1.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- feat: ai
|
|
8
|
+
- @fe-free/core@4.1.11
|
|
9
|
+
- @fe-free/icons@4.1.11
|
|
10
|
+
- @fe-free/tool@4.1.11
|
|
11
|
+
|
|
12
|
+
## 4.1.10
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- feat: ai
|
|
17
|
+
- Updated dependencies
|
|
18
|
+
- @fe-free/core@4.1.10
|
|
19
|
+
- @fe-free/icons@4.1.10
|
|
20
|
+
- @fe-free/tool@4.1.10
|
|
21
|
+
|
|
3
22
|
## 4.1.9
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fe-free/ai",
|
|
3
|
-
"version": "4.1.
|
|
3
|
+
"version": "4.1.11",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"author": "",
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"lodash-es": "^4.17.21",
|
|
16
16
|
"uuid": "^13.0.0",
|
|
17
17
|
"zustand": "^4.5.7",
|
|
18
|
-
"@fe-free/core": "4.1.
|
|
18
|
+
"@fe-free/core": "4.1.11"
|
|
19
19
|
},
|
|
20
20
|
"peerDependencies": {
|
|
21
21
|
"@ant-design/x-sdk": "^2.1.3",
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"i18next-icu": "^2.4.1",
|
|
27
27
|
"react": "^19.2.0",
|
|
28
28
|
"react-i18next": "^16.4.0",
|
|
29
|
-
"@fe-free/
|
|
30
|
-
"@fe-free/
|
|
29
|
+
"@fe-free/icons": "4.1.11",
|
|
30
|
+
"@fe-free/tool": "4.1.11"
|
|
31
31
|
},
|
|
32
32
|
"scripts": {
|
|
33
33
|
"test": "echo \"Error: no test specified\" && exit 1",
|
package/src/ai.stories.tsx
CHANGED
|
@@ -3,12 +3,16 @@ import {
|
|
|
3
3
|
Chat,
|
|
4
4
|
createChatStore,
|
|
5
5
|
EnumChatMessageStatus,
|
|
6
|
+
EnumChatMessageType,
|
|
6
7
|
generateUUID,
|
|
8
|
+
MessageActions,
|
|
7
9
|
Messages,
|
|
8
10
|
MSender,
|
|
9
11
|
} from '@fe-free/ai';
|
|
10
12
|
import { sleep } from '@fe-free/tool';
|
|
11
13
|
import type { Meta } from '@storybook/react-vite';
|
|
14
|
+
import { useDebounce, useUpdateEffect } from 'ahooks';
|
|
15
|
+
import { Button, Divider } from 'antd';
|
|
12
16
|
import { set } from 'lodash-es';
|
|
13
17
|
import { useCallback, useEffect, useMemo } from 'react';
|
|
14
18
|
|
|
@@ -45,37 +49,27 @@ function Component() {
|
|
|
45
49
|
const senderValue = useChatStore((state) => state.senderValue);
|
|
46
50
|
const setSenderValue = useChatStore((state) => state.setSenderValue);
|
|
47
51
|
const messages = useChatStore((state) => state.messages);
|
|
52
|
+
const setMessages = useChatStore((state) => state.setMessages);
|
|
48
53
|
const addMessage = useChatStore((state) => state.addMessage);
|
|
49
54
|
const updateMessage = useChatStore((state) => state.updateMessage);
|
|
50
55
|
const { chatStatus } = useChatStoreComputed();
|
|
51
56
|
|
|
52
|
-
const
|
|
53
|
-
chatStatus === EnumChatMessageStatus.PENDING || chatStatus === EnumChatMessageStatus.STREAMING;
|
|
57
|
+
const debounceCacheMessages = useDebounce(messages, { wait: 500 });
|
|
54
58
|
|
|
59
|
+
// init from cache
|
|
55
60
|
useEffect(() => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
},
|
|
61
|
-
ai: {
|
|
62
|
-
data: {
|
|
63
|
-
text: '你好,\n我是AI,很高兴认识你',
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
});
|
|
67
|
-
addMessage({
|
|
68
|
-
uuid: generateUUID(),
|
|
69
|
-
user: {
|
|
70
|
-
text: 'hello',
|
|
71
|
-
},
|
|
72
|
-
ai: {
|
|
73
|
-
data: {
|
|
74
|
-
text: '你\n好,\n我\n是\nAI,\n很\n高\n兴\n认\n识\n你\n你\n好,\n我\n是\nAI,\n很\n高\n兴\n认\n识\n你\n很\n高\n兴\n认\n识\n你\n',
|
|
75
|
-
},
|
|
76
|
-
},
|
|
77
|
-
});
|
|
61
|
+
const cacheMessages = localStorage.getItem('chatMessages');
|
|
62
|
+
if (cacheMessages) {
|
|
63
|
+
setMessages(JSON.parse(cacheMessages));
|
|
64
|
+
}
|
|
78
65
|
}, []);
|
|
66
|
+
// cache
|
|
67
|
+
useUpdateEffect(() => {
|
|
68
|
+
localStorage.setItem('chatMessages', JSON.stringify(debounceCacheMessages));
|
|
69
|
+
}, [debounceCacheMessages]);
|
|
70
|
+
|
|
71
|
+
const loading =
|
|
72
|
+
chatStatus === EnumChatMessageStatus.PENDING || chatStatus === EnumChatMessageStatus.STREAMING;
|
|
79
73
|
|
|
80
74
|
const handleSubmit = useCallback((v) => {
|
|
81
75
|
console.log('onSubmit', v);
|
|
@@ -99,49 +93,101 @@ function Component() {
|
|
|
99
93
|
const preText = message.ai?.data?.text || '';
|
|
100
94
|
set(message, 'ai.data.text', preText + data);
|
|
101
95
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
96
|
+
// 假设有 session_id
|
|
97
|
+
set(message, 'ai.session_id', '123');
|
|
98
|
+
|
|
99
|
+
updateMessage(message);
|
|
105
100
|
}
|
|
106
101
|
if (event === 'done') {
|
|
107
102
|
message.status = EnumChatMessageStatus.DONE;
|
|
108
|
-
updateMessage(
|
|
109
|
-
...message,
|
|
110
|
-
});
|
|
103
|
+
updateMessage(message);
|
|
111
104
|
}
|
|
112
105
|
},
|
|
113
106
|
});
|
|
114
107
|
}, []);
|
|
115
108
|
|
|
116
109
|
return (
|
|
117
|
-
<div
|
|
118
|
-
<
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
<div
|
|
139
|
-
|
|
140
|
-
|
|
110
|
+
<div>
|
|
111
|
+
<div>
|
|
112
|
+
<Button
|
|
113
|
+
onClick={() => {
|
|
114
|
+
addMessage({
|
|
115
|
+
uuid: generateUUID(),
|
|
116
|
+
type: EnumChatMessageType.SYSTEM,
|
|
117
|
+
system: {
|
|
118
|
+
data: {
|
|
119
|
+
type: 'new_session',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
}}
|
|
124
|
+
>
|
|
125
|
+
Add New Session
|
|
126
|
+
</Button>
|
|
127
|
+
</div>
|
|
128
|
+
<div className="h-[800px] w-[500px] border border-red-500">
|
|
129
|
+
<Chat
|
|
130
|
+
end={
|
|
131
|
+
<div
|
|
132
|
+
className="p-2"
|
|
133
|
+
onFocus={() => {
|
|
134
|
+
console.log('onFocus');
|
|
135
|
+
}}
|
|
136
|
+
onBlur={() => {
|
|
137
|
+
console.log('onBlur');
|
|
138
|
+
}}
|
|
139
|
+
>
|
|
140
|
+
<MSender
|
|
141
|
+
value={senderValue}
|
|
142
|
+
onChange={(v) => setSenderValue(v)}
|
|
143
|
+
loading={loading}
|
|
144
|
+
onSubmit={handleSubmit}
|
|
145
|
+
/>
|
|
141
146
|
</div>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
}
|
|
148
|
+
>
|
|
149
|
+
<Messages
|
|
150
|
+
messages={messages}
|
|
151
|
+
renderMessageOfSystem={({ message }) => {
|
|
152
|
+
if (message.system?.data?.type === 'new_session') {
|
|
153
|
+
return <Divider>让我们聊点新内容吧</Divider>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return null;
|
|
157
|
+
}}
|
|
158
|
+
renderMessageOfUser={({ message }) => {
|
|
159
|
+
return (
|
|
160
|
+
<div className="p-2">
|
|
161
|
+
<div className="rounded-xl bg-primary p-2 text-white">{message.user?.text}</div>
|
|
162
|
+
</div>
|
|
163
|
+
);
|
|
164
|
+
}}
|
|
165
|
+
renderMessageOfAI={({ message }) => {
|
|
166
|
+
return (
|
|
167
|
+
<div className="p-2">
|
|
168
|
+
<div>
|
|
169
|
+
status: {message.status} session_id: {message.ai?.session_id}
|
|
170
|
+
</div>
|
|
171
|
+
<pre className="whitespace-pre-wrap">{message.ai?.data?.text}</pre>
|
|
172
|
+
<div className="flex gap-2">
|
|
173
|
+
<MessageActions.Copy value={message.ai?.data?.text || ''} />
|
|
174
|
+
<MessageActions.Like
|
|
175
|
+
onClick={async () => {
|
|
176
|
+
// some thing
|
|
177
|
+
}}
|
|
178
|
+
/>
|
|
179
|
+
<MessageActions.Dislike
|
|
180
|
+
onClick={async () => {
|
|
181
|
+
// some thing
|
|
182
|
+
}}
|
|
183
|
+
/>
|
|
184
|
+
</div>
|
|
185
|
+
</div>
|
|
186
|
+
);
|
|
187
|
+
}}
|
|
188
|
+
/>
|
|
189
|
+
</Chat>
|
|
190
|
+
</div>
|
|
145
191
|
</div>
|
|
146
192
|
);
|
|
147
193
|
}
|
package/src/helper.tsx
CHANGED
|
@@ -2,12 +2,19 @@ import classNames from 'classnames';
|
|
|
2
2
|
import { range } from 'lodash-es';
|
|
3
3
|
import { v4 as uuidv4 } from 'uuid';
|
|
4
4
|
|
|
5
|
+
function getAnimationDelay(index: number) {
|
|
6
|
+
const arr = [0.1, 0.2, 0.4, 0.6, 0.8, 0.9];
|
|
7
|
+
return arr[index % arr.length];
|
|
8
|
+
}
|
|
9
|
+
|
|
5
10
|
function RecordLoading({
|
|
6
11
|
count = 4,
|
|
7
12
|
gap = 2,
|
|
8
13
|
color = 'white',
|
|
9
14
|
}: {
|
|
10
15
|
count?: number;
|
|
16
|
+
height?: number;
|
|
17
|
+
width?: number;
|
|
11
18
|
gap?: number;
|
|
12
19
|
color?: 'white' | 'primary';
|
|
13
20
|
}) {
|
|
@@ -16,12 +23,14 @@ function RecordLoading({
|
|
|
16
23
|
{range(count).map((index) => (
|
|
17
24
|
<div
|
|
18
25
|
key={index}
|
|
19
|
-
className={classNames(
|
|
26
|
+
className={classNames({
|
|
20
27
|
'bg-white': color === 'white',
|
|
21
28
|
'bg-primary': color === 'primary',
|
|
22
29
|
})}
|
|
23
30
|
style={{
|
|
24
|
-
|
|
31
|
+
height: '4px',
|
|
32
|
+
width: '2px',
|
|
33
|
+
animation: `sender-rectangle-${color} infinite 1s ease-in-out ${getAnimationDelay(index)}s`,
|
|
25
34
|
}}
|
|
26
35
|
/>
|
|
27
36
|
))}
|
package/src/index.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { FileView, FileViewList } from './files';
|
|
|
4
4
|
export { generateUUID } from './helper';
|
|
5
5
|
export { MSender } from './m_sender';
|
|
6
6
|
export type { MSenderProps, MSenderRef } from './m_sender';
|
|
7
|
-
export { Messages } from './messages';
|
|
7
|
+
export { MessageActions, Messages } from './messages';
|
|
8
8
|
export type { MessagesProps } from './messages';
|
|
9
9
|
export { Sender } from './sender';
|
|
10
10
|
export type { SenderProps, SenderRef } from './sender';
|
package/src/m_sender/actions.tsx
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
import Icons from '@fe-free/icons';
|
|
2
2
|
import { Button } from 'antd';
|
|
3
3
|
import { useCallback, type RefObject } from 'react';
|
|
4
|
+
import IconRecord from '../svgs/record.svg?react';
|
|
4
5
|
import SendIcon from '../svgs/send.svg?react';
|
|
5
6
|
import type { MSenderProps } from './types';
|
|
6
7
|
|
|
7
8
|
function Actions(
|
|
8
9
|
props: MSenderProps & {
|
|
9
10
|
refText: RefObject<HTMLTextAreaElement>;
|
|
11
|
+
type: 'input' | 'record';
|
|
12
|
+
setType: (type: 'input' | 'record') => void;
|
|
10
13
|
},
|
|
11
14
|
) {
|
|
12
|
-
const { refText, loading, onSubmit, value, onChange } = props;
|
|
15
|
+
const { refText, loading, onSubmit, value, onChange, setType, allowSpeech } = props;
|
|
13
16
|
|
|
14
17
|
const isLoading = loading;
|
|
15
18
|
|
|
16
19
|
const handleSubmit = useCallback(async () => {
|
|
17
|
-
if (isLoading) {
|
|
20
|
+
if (isLoading || value?.text?.trim() === '') {
|
|
18
21
|
return;
|
|
19
22
|
}
|
|
20
23
|
|
|
@@ -36,16 +39,23 @@ function Actions(
|
|
|
36
39
|
}, [isLoading, value, onSubmit, onChange, refText]);
|
|
37
40
|
|
|
38
41
|
return (
|
|
39
|
-
<div className="flex items-center gap-2">
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
<div className="mr-1 flex items-center gap-2">
|
|
43
|
+
{allowSpeech && !value?.text ? (
|
|
44
|
+
<Button
|
|
45
|
+
type="primary"
|
|
46
|
+
shape="circle"
|
|
47
|
+
icon={<Icons component={IconRecord} />}
|
|
48
|
+
onClick={() => setType('record')}
|
|
49
|
+
/>
|
|
50
|
+
) : (
|
|
51
|
+
<Button
|
|
52
|
+
type="primary"
|
|
53
|
+
shape="circle"
|
|
54
|
+
icon={<Icons component={SendIcon} />}
|
|
55
|
+
loading={isLoading}
|
|
56
|
+
onClick={handleSubmit}
|
|
57
|
+
/>
|
|
58
|
+
)}
|
|
49
59
|
</div>
|
|
50
60
|
);
|
|
51
61
|
}
|
package/src/m_sender/index.tsx
CHANGED
|
@@ -3,22 +3,23 @@ import classNames from 'classnames';
|
|
|
3
3
|
import { useMemo, useRef, useState, type RefObject } from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
5
|
import { Actions } from './actions';
|
|
6
|
-
import {
|
|
6
|
+
import { RecordAction } from './record';
|
|
7
7
|
import type { MSenderProps, MSenderRef } from './types';
|
|
8
8
|
|
|
9
9
|
function Text(props: MSenderProps & { refText: RefObject<HTMLTextAreaElement> }) {
|
|
10
|
-
const { value, onChange, placeholder, refText } = props;
|
|
10
|
+
const { value, onChange, placeholder, refText, autoFocus } = props;
|
|
11
11
|
|
|
12
12
|
return (
|
|
13
13
|
<Input.TextArea
|
|
14
14
|
ref={refText}
|
|
15
15
|
value={value?.text}
|
|
16
|
+
autoFocus={autoFocus}
|
|
16
17
|
onChange={(e) => {
|
|
17
18
|
onChange?.({ ...value, text: e.target.value });
|
|
18
19
|
}}
|
|
19
20
|
placeholder={placeholder}
|
|
20
21
|
autoSize={{ minRows: 1, maxRows: 3 }}
|
|
21
|
-
className="
|
|
22
|
+
className="px-1 text-[15px]"
|
|
22
23
|
variant="borderless"
|
|
23
24
|
/>
|
|
24
25
|
);
|
|
@@ -32,12 +33,6 @@ function useProps(originProps: MSenderProps) {
|
|
|
32
33
|
...originProps,
|
|
33
34
|
placeholder:
|
|
34
35
|
originProps.placeholder ?? t('@fe-free/ai.sender.describeYourQuestion', '描述你的问题'),
|
|
35
|
-
statement:
|
|
36
|
-
originProps.statement ??
|
|
37
|
-
t(
|
|
38
|
-
'@fe-free/ai.sender.aiGeneratedDisclaimer',
|
|
39
|
-
'内容由 AI 生成,无法确保信息的真实准确,仅供参考',
|
|
40
|
-
),
|
|
41
36
|
defaultType: originProps.defaultType ?? 'input',
|
|
42
37
|
};
|
|
43
38
|
}, [originProps, t]);
|
|
@@ -47,7 +42,7 @@ function MSender(originProps: MSenderProps) {
|
|
|
47
42
|
const refText = useRef<HTMLTextAreaElement>(null);
|
|
48
43
|
|
|
49
44
|
const props = useProps(originProps);
|
|
50
|
-
const {
|
|
45
|
+
const { statement, defaultType } = props;
|
|
51
46
|
|
|
52
47
|
const [type, setType] = useState<'input' | 'record'>(defaultType);
|
|
53
48
|
|
|
@@ -58,16 +53,13 @@ function MSender(originProps: MSenderProps) {
|
|
|
58
53
|
<div
|
|
59
54
|
ref={refContainer}
|
|
60
55
|
className={classNames(
|
|
61
|
-
'fea-m-sender relative flex items-end rounded-xl border border-01 bg-white p-2',
|
|
56
|
+
'fea-m-sender relative flex items-end rounded-xl border border-01 bg-white p-2.5',
|
|
62
57
|
)}
|
|
63
58
|
>
|
|
64
|
-
{allowSpeech && !value?.text && (
|
|
65
|
-
<InputRecordSwitch {...props} type={type} setType={setType} />
|
|
66
|
-
)}
|
|
67
59
|
<div className="flex flex-1">
|
|
68
60
|
<Text {...props} refText={refText} />
|
|
69
61
|
</div>
|
|
70
|
-
<Actions {...props} refText={refText} />
|
|
62
|
+
<Actions {...props} refText={refText} type={type} setType={setType} />
|
|
71
63
|
{type === 'record' && <RecordAction {...props} setType={setType} />}
|
|
72
64
|
</div>
|
|
73
65
|
{statement && <div className="mt-1 text-center text-xs text-04">*{statement}</div>}
|
|
@@ -12,7 +12,7 @@ const meta: Meta<typeof MSender> = {
|
|
|
12
12
|
|
|
13
13
|
type Story = StoryObj<typeof MSender>;
|
|
14
14
|
|
|
15
|
-
function Component(props:
|
|
15
|
+
function Component(props: MSenderProps) {
|
|
16
16
|
const [v, setV] = useState<MSenderValue | undefined>(undefined);
|
|
17
17
|
|
|
18
18
|
return (
|
|
@@ -39,6 +39,16 @@ export const Default: Story = {
|
|
|
39
39
|
},
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
+
export const AutoFocus: Story = {
|
|
43
|
+
render: (props) => <Component {...props} />,
|
|
44
|
+
args: {
|
|
45
|
+
autoFocus: true,
|
|
46
|
+
onSubmit: (value) => {
|
|
47
|
+
console.log(value);
|
|
48
|
+
},
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
|
|
42
52
|
export const Loading: Story = {
|
|
43
53
|
args: {
|
|
44
54
|
loading: true,
|
|
@@ -73,14 +83,13 @@ export const AllowSpeech: Story = {
|
|
|
73
83
|
const recordResult = recordVoice;
|
|
74
84
|
|
|
75
85
|
handleSubmit({ ...(props.value || {}), text: recordResult });
|
|
76
|
-
alert('submit');
|
|
77
86
|
}
|
|
78
87
|
},
|
|
79
88
|
[props.value, recordVoice],
|
|
80
89
|
);
|
|
81
90
|
|
|
82
91
|
return (
|
|
83
|
-
<div>
|
|
92
|
+
<div className="flex flex-col gap-10">
|
|
84
93
|
<Component
|
|
85
94
|
{...props}
|
|
86
95
|
allowSpeech={{
|
|
@@ -90,19 +99,21 @@ export const AllowSpeech: Story = {
|
|
|
90
99
|
/>
|
|
91
100
|
|
|
92
101
|
<Component
|
|
102
|
+
value={{ text: 'test' } as MSenderValue}
|
|
93
103
|
{...props}
|
|
94
|
-
defaultType="record"
|
|
95
104
|
allowSpeech={{
|
|
96
105
|
onRecordStart: handleRecordStart,
|
|
97
106
|
onRecordEnd: handleRecordEnd,
|
|
98
107
|
}}
|
|
99
108
|
/>
|
|
100
109
|
|
|
110
|
+
<div />
|
|
111
|
+
|
|
101
112
|
<Component
|
|
102
113
|
{...props}
|
|
103
114
|
defaultType="record"
|
|
104
115
|
allowSpeech={{
|
|
105
|
-
onRecordStart:
|
|
116
|
+
onRecordStart: handleRecordStart,
|
|
106
117
|
onRecordEnd: handleRecordEnd,
|
|
107
118
|
}}
|
|
108
119
|
/>
|
package/src/m_sender/record.tsx
CHANGED
|
@@ -4,41 +4,8 @@ import classNames from 'classnames';
|
|
|
4
4
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
5
5
|
import { RecordLoading } from '../helper';
|
|
6
6
|
import IconKeyboard from '../svgs/keyboard.svg?react';
|
|
7
|
-
import IconRecord from '../svgs/record.svg?react';
|
|
8
7
|
import type { MSenderProps } from './types';
|
|
9
8
|
|
|
10
|
-
function InputRecordSwitch(
|
|
11
|
-
props: MSenderProps & { type: 'input' | 'record'; setType: (type: 'input' | 'record') => void },
|
|
12
|
-
) {
|
|
13
|
-
const { allowSpeech, type, setType } = props;
|
|
14
|
-
|
|
15
|
-
if (!allowSpeech) {
|
|
16
|
-
return null;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
if (type === 'record') {
|
|
20
|
-
return (
|
|
21
|
-
<Button
|
|
22
|
-
type="text"
|
|
23
|
-
shape="circle"
|
|
24
|
-
size="small"
|
|
25
|
-
icon={<Icons component={IconKeyboard} className="!text-[24px]" />}
|
|
26
|
-
onClick={() => setType('input')}
|
|
27
|
-
/>
|
|
28
|
-
);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<Button
|
|
33
|
-
type="text"
|
|
34
|
-
shape="circle"
|
|
35
|
-
size="small"
|
|
36
|
-
icon={<Icons component={IconRecord} className="!text-[24px]" />}
|
|
37
|
-
onClick={() => setType('record')}
|
|
38
|
-
/>
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
9
|
function RecordAction(props: MSenderProps & { setType }) {
|
|
43
10
|
const { allowSpeech, setType } = props;
|
|
44
11
|
|
|
@@ -160,37 +127,35 @@ function RecordAction(props: MSenderProps & { setType }) {
|
|
|
160
127
|
return (
|
|
161
128
|
<div
|
|
162
129
|
className={classNames('absolute inset-0 flex items-center justify-center rounded-xl', {
|
|
163
|
-
'bg-
|
|
164
|
-
'bg-
|
|
165
|
-
'bg-primary': isRecording && !isCancel,
|
|
130
|
+
'bg-red-500': isCancel,
|
|
131
|
+
'bg-primary': !isCancel,
|
|
166
132
|
})}
|
|
167
133
|
>
|
|
168
134
|
{isRecording ? (
|
|
169
135
|
<>
|
|
170
136
|
<RecordLoading count={30} gap={4} />
|
|
171
|
-
{isCancel && <div className="absolute top-0 -mt-[
|
|
137
|
+
{isCancel && <div className="absolute top-0 -mt-[2.5em] text-red08">松开取消</div>}
|
|
172
138
|
{!isCancel && (
|
|
173
|
-
<div className="absolute top-0 -mt-[
|
|
139
|
+
<div className="absolute top-0 -mt-[2.5em] text-03">松开发送,上移取消</div>
|
|
174
140
|
)}
|
|
175
141
|
</>
|
|
176
142
|
) : (
|
|
177
|
-
<div>按住说话</div>
|
|
143
|
+
<div className="text-base text-white">按住说话</div>
|
|
178
144
|
)}
|
|
179
145
|
<div className="absolute inset-0" ref={containerRef} />
|
|
180
146
|
{!isRecording && (
|
|
181
147
|
<Button
|
|
182
148
|
type="text"
|
|
183
149
|
shape="circle"
|
|
184
|
-
|
|
185
|
-
icon={<Icons component={IconKeyboard} className="!text-[24px]" />}
|
|
150
|
+
icon={<Icons component={IconKeyboard} className="!text-xl text-white" />}
|
|
186
151
|
onClick={() => {
|
|
187
152
|
setType('input');
|
|
188
153
|
}}
|
|
189
|
-
className="absolute
|
|
154
|
+
className="absolute right-4"
|
|
190
155
|
/>
|
|
191
156
|
)}
|
|
192
157
|
</div>
|
|
193
158
|
);
|
|
194
159
|
}
|
|
195
160
|
|
|
196
|
-
export {
|
|
161
|
+
export { RecordAction };
|
package/src/m_sender/types.ts
CHANGED
|
@@ -14,6 +14,7 @@ interface MSenderProps {
|
|
|
14
14
|
loading?: boolean;
|
|
15
15
|
onSubmit?: (value?: MSenderValue) => void | Promise<void>;
|
|
16
16
|
|
|
17
|
+
autoFocus?: boolean;
|
|
17
18
|
placeholder?: string;
|
|
18
19
|
|
|
19
20
|
/** 是否允许语音输入 */
|
|
@@ -25,7 +26,7 @@ interface MSenderProps {
|
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
defaultType?: 'input' | 'record';
|
|
28
|
-
statement?: string
|
|
29
|
+
statement?: string;
|
|
29
30
|
}
|
|
30
31
|
|
|
31
32
|
export type { MSenderProps, MSenderRef, MSenderValue };
|
package/src/messages/index.tsx
CHANGED
|
@@ -1,68 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
interface MessagesProps<AIData> {
|
|
6
|
-
messages?: ChatMessage<AIData>[];
|
|
7
|
-
renderMessageOfUser?: (props: { message: ChatMessage<AIData> }) => React.ReactNode;
|
|
8
|
-
renderMessageOfAI?: (props: { message: ChatMessage<AIData> }) => React.ReactNode;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function Messages<AIData>(props: MessagesProps<AIData>) {
|
|
12
|
-
const { messages, renderMessageOfUser, renderMessageOfAI } = props;
|
|
13
|
-
|
|
14
|
-
const ref = useRef<HTMLDivElement>(null);
|
|
15
|
-
|
|
16
|
-
const lastMessage = useMemo(() => {
|
|
17
|
-
return messages?.[messages.length - 1];
|
|
18
|
-
}, [messages]);
|
|
19
|
-
|
|
20
|
-
// 首次和更新时滚动到最新消息
|
|
21
|
-
useEffect(() => {
|
|
22
|
-
if (!lastMessage) {
|
|
23
|
-
return;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
const element = document.querySelector(`[data-uuid="${lastMessage.uuid}"]`);
|
|
27
|
-
if (element) {
|
|
28
|
-
element.scrollIntoView({ behavior: 'smooth' });
|
|
29
|
-
}
|
|
30
|
-
}, [lastMessage?.uuid]);
|
|
31
|
-
|
|
32
|
-
// 数据更新是,如果 dom 处于可视区域,则滚动
|
|
33
|
-
useEffect(() => {
|
|
34
|
-
if (!lastMessage || !ref.current) {
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const element = document.querySelector(`[data-uuid="${lastMessage.uuid}"]`);
|
|
39
|
-
if (!element) {
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const { top: listTop, bottom: listBottom } = ref.current!.getBoundingClientRect();
|
|
44
|
-
const { top, bottom } = element.getBoundingClientRect();
|
|
45
|
-
|
|
46
|
-
// 如果最后一个元素可见,则滚动到底部
|
|
47
|
-
const isVisible = top < listBottom && bottom > listTop;
|
|
48
|
-
if (isVisible) {
|
|
49
|
-
element.scrollIntoView({ behavior: 'smooth' });
|
|
50
|
-
}
|
|
51
|
-
}, [lastMessage?.updatedAt]);
|
|
52
|
-
|
|
53
|
-
return (
|
|
54
|
-
<PageLayout>
|
|
55
|
-
<div ref={ref} className="flex h-full flex-col overflow-y-auto">
|
|
56
|
-
{messages?.map((message) => (
|
|
57
|
-
<div key={message.uuid} data-uuid={message.uuid} className="flex flex-col">
|
|
58
|
-
<div className="flex justify-end">{renderMessageOfUser?.({ message })}</div>
|
|
59
|
-
<div className="flex justify-start">{renderMessageOfAI?.({ message })}</div>
|
|
60
|
-
</div>
|
|
61
|
-
))}
|
|
62
|
-
</div>
|
|
63
|
-
</PageLayout>
|
|
64
|
-
);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { Messages };
|
|
68
|
-
export type { MessagesProps };
|
|
1
|
+
export { MessageActions } from './message_actions';
|
|
2
|
+
export { Messages } from './messages';
|
|
3
|
+
export type { MessagesProps } from './messages';
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Copy } from '@fe-free/core';
|
|
2
|
+
import {
|
|
3
|
+
CopyFilled,
|
|
4
|
+
CopyOutlined,
|
|
5
|
+
DislikeFilled,
|
|
6
|
+
DislikeOutlined,
|
|
7
|
+
LikeFilled,
|
|
8
|
+
LikeOutlined,
|
|
9
|
+
} from '@fe-free/icons';
|
|
10
|
+
import { App, Button, Tooltip } from 'antd';
|
|
11
|
+
import { useCallback, useState } from 'react';
|
|
12
|
+
|
|
13
|
+
function MessageActionOfCopy({ value, onCopied }: { value: string; onCopied?: () => void }) {
|
|
14
|
+
const [active, setActive] = useState(false);
|
|
15
|
+
const { message } = App.useApp();
|
|
16
|
+
|
|
17
|
+
const handleCopied = useCallback(async () => {
|
|
18
|
+
setActive(true);
|
|
19
|
+
onCopied?.();
|
|
20
|
+
message.success('复制成功');
|
|
21
|
+
}, [onCopied, message]);
|
|
22
|
+
|
|
23
|
+
return (
|
|
24
|
+
<Tooltip title="复制">
|
|
25
|
+
<Copy value={value} className="cursor-pointer text-03" onCopied={handleCopied}>
|
|
26
|
+
<Button
|
|
27
|
+
type="text"
|
|
28
|
+
size="small"
|
|
29
|
+
className="text-03"
|
|
30
|
+
icon={active ? <CopyFilled /> : <CopyOutlined />}
|
|
31
|
+
/>
|
|
32
|
+
</Copy>
|
|
33
|
+
</Tooltip>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function MessageActionOfLike({ onClick }: { onClick?: (active: boolean) => Promise<void> }) {
|
|
38
|
+
const [active, setActive] = useState(false);
|
|
39
|
+
|
|
40
|
+
const handleClick = useCallback(async () => {
|
|
41
|
+
await Promise.resolve(onClick?.(!active));
|
|
42
|
+
setActive(!active);
|
|
43
|
+
}, [onClick, active]);
|
|
44
|
+
|
|
45
|
+
return (
|
|
46
|
+
<Tooltip title={active ? '取消点赞' : '点赞'}>
|
|
47
|
+
<Button
|
|
48
|
+
type="text"
|
|
49
|
+
onClick={handleClick}
|
|
50
|
+
size="small"
|
|
51
|
+
className="text-03"
|
|
52
|
+
icon={active ? <LikeFilled /> : <LikeOutlined />}
|
|
53
|
+
/>
|
|
54
|
+
</Tooltip>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function MessageActionOfDislike({ onClick }: { onClick?: (active: boolean) => Promise<void> }) {
|
|
59
|
+
const [active, setActive] = useState(false);
|
|
60
|
+
|
|
61
|
+
const handleClick = useCallback(async () => {
|
|
62
|
+
await Promise.resolve(onClick?.(!active));
|
|
63
|
+
setActive(!active);
|
|
64
|
+
}, [onClick, active]);
|
|
65
|
+
|
|
66
|
+
return (
|
|
67
|
+
<Tooltip title={active ? '取消点踩' : '点踩'}>
|
|
68
|
+
<Button
|
|
69
|
+
type="text"
|
|
70
|
+
onClick={handleClick}
|
|
71
|
+
size="small"
|
|
72
|
+
className="text-03"
|
|
73
|
+
icon={active ? <DislikeFilled /> : <DislikeOutlined />}
|
|
74
|
+
/>
|
|
75
|
+
</Tooltip>
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const MessageActions = {
|
|
80
|
+
Copy: MessageActionOfCopy,
|
|
81
|
+
Like: MessageActionOfLike,
|
|
82
|
+
Dislike: MessageActionOfDislike,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export { MessageActions };
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { PageLayout } from '@fe-free/core';
|
|
2
|
+
import { useEffect, useMemo, useRef } from 'react';
|
|
3
|
+
import { EnumChatMessageType, type ChatMessage } from '../store/types';
|
|
4
|
+
|
|
5
|
+
interface MessagesProps<AIData> {
|
|
6
|
+
messages?: ChatMessage<AIData>[];
|
|
7
|
+
/** 含所有 */
|
|
8
|
+
renderMessage?: (props: { message: ChatMessage<AIData> }) => React.ReactNode;
|
|
9
|
+
/** 系统消息 */
|
|
10
|
+
renderMessageOfSystem?: (props: { message: ChatMessage<AIData> }) => React.ReactNode;
|
|
11
|
+
/** 用户消息 */
|
|
12
|
+
renderMessageOfUser?: (props: { message: ChatMessage<AIData> }) => React.ReactNode;
|
|
13
|
+
/** AI消息 */
|
|
14
|
+
renderMessageOfAI?: (props: { message: ChatMessage<AIData> }) => React.ReactNode;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function Messages<AIData>(props: MessagesProps<AIData>) {
|
|
18
|
+
const { messages, renderMessage, renderMessageOfSystem, renderMessageOfUser, renderMessageOfAI } =
|
|
19
|
+
props;
|
|
20
|
+
|
|
21
|
+
const ref = useRef<HTMLDivElement>(null);
|
|
22
|
+
|
|
23
|
+
const lastMessage = useMemo(() => {
|
|
24
|
+
return messages?.[messages.length - 1];
|
|
25
|
+
}, [messages]);
|
|
26
|
+
|
|
27
|
+
// 首次和更新时滚动到最新消息
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
if (!lastMessage?.uuid) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const element = document.querySelector(`[data-uuid="${lastMessage.uuid}"]`);
|
|
34
|
+
if (element) {
|
|
35
|
+
element.scrollIntoView({ behavior: 'smooth' });
|
|
36
|
+
}
|
|
37
|
+
}, [lastMessage?.uuid]);
|
|
38
|
+
|
|
39
|
+
// 数据更新是,如果 dom 处于可视区域,则滚动
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!lastMessage?.updatedAt || !lastMessage?.uuid || !ref.current) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const element = document.querySelector(`[data-uuid="${lastMessage.uuid}"]`);
|
|
46
|
+
if (!element) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const { top: listTop, bottom: listBottom } = ref.current!.getBoundingClientRect();
|
|
51
|
+
const { top, bottom } = element.getBoundingClientRect();
|
|
52
|
+
|
|
53
|
+
// 如果最后一个元素可见,则滚动到底部
|
|
54
|
+
const isVisible = top < listBottom && bottom > listTop;
|
|
55
|
+
if (isVisible) {
|
|
56
|
+
element.scrollIntoView({ behavior: 'smooth' });
|
|
57
|
+
}
|
|
58
|
+
}, [lastMessage?.updatedAt, lastMessage?.uuid]);
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<PageLayout>
|
|
62
|
+
<div ref={ref} className="flex h-full flex-col overflow-y-auto">
|
|
63
|
+
{messages?.map((message) => {
|
|
64
|
+
return (
|
|
65
|
+
<div key={message.uuid} data-uuid={message.uuid} className="flex flex-col">
|
|
66
|
+
{renderMessage ? (
|
|
67
|
+
renderMessage?.({ message })
|
|
68
|
+
) : (
|
|
69
|
+
<>
|
|
70
|
+
{message.type === EnumChatMessageType.SYSTEM && message.system && (
|
|
71
|
+
<div className="flex justify-center">
|
|
72
|
+
{renderMessageOfSystem?.({ message })}
|
|
73
|
+
</div>
|
|
74
|
+
)}
|
|
75
|
+
{message.type !== EnumChatMessageType.SYSTEM && message.user && (
|
|
76
|
+
<div className="flex justify-end">{renderMessageOfUser?.({ message })}</div>
|
|
77
|
+
)}
|
|
78
|
+
{message.type !== EnumChatMessageType.SYSTEM && message.ai && (
|
|
79
|
+
<div className="flex justify-start">{renderMessageOfAI?.({ message })}</div>
|
|
80
|
+
)}
|
|
81
|
+
</>
|
|
82
|
+
)}
|
|
83
|
+
</div>
|
|
84
|
+
);
|
|
85
|
+
})}
|
|
86
|
+
</div>
|
|
87
|
+
</PageLayout>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export { Messages };
|
|
92
|
+
export type { MessagesProps };
|
package/src/store/index.ts
CHANGED
|
@@ -2,30 +2,47 @@ import { useMemo } from 'react';
|
|
|
2
2
|
import { create } from 'zustand';
|
|
3
3
|
import type { ChatMessage } from './types';
|
|
4
4
|
|
|
5
|
-
interface
|
|
5
|
+
interface BaseSenderValue {
|
|
6
|
+
text?: string;
|
|
7
|
+
files?: string[];
|
|
8
|
+
}
|
|
9
|
+
interface ChatStore<Value extends BaseSenderValue | undefined, AIData> {
|
|
6
10
|
senderValue?: Value;
|
|
7
11
|
setSenderValue: (senderValue?: Value) => void;
|
|
8
12
|
|
|
9
13
|
messages: ChatMessage<AIData>[];
|
|
14
|
+
setMessages: (messages: ChatMessage<AIData>[]) => void;
|
|
10
15
|
addMessage: (message: ChatMessage<AIData>) => void;
|
|
11
16
|
updateMessage: (message: ChatMessage<AIData>) => void;
|
|
12
17
|
|
|
13
18
|
reset: () => void;
|
|
14
19
|
}
|
|
15
20
|
|
|
16
|
-
function createChatStore<Value, AIData>() {
|
|
21
|
+
function createChatStore<Value extends BaseSenderValue | undefined, AIData>() {
|
|
17
22
|
const useChatStore = create<ChatStore<Value, AIData>>((set, get, store) => ({
|
|
18
23
|
senderValue: undefined,
|
|
19
24
|
setSenderValue: (senderValue) => {
|
|
20
25
|
set(() => ({ senderValue }));
|
|
21
26
|
},
|
|
22
27
|
messages: [],
|
|
28
|
+
setMessages: (messages) => {
|
|
29
|
+
set(() => ({
|
|
30
|
+
messages: messages.map((message) => ({
|
|
31
|
+
// 如果没有,则用当前的时间
|
|
32
|
+
createdAt: Date.now(),
|
|
33
|
+
updatedAt: Date.now(),
|
|
34
|
+
...message,
|
|
35
|
+
})),
|
|
36
|
+
}));
|
|
37
|
+
},
|
|
23
38
|
addMessage: (message) => {
|
|
24
39
|
set((state) => ({
|
|
25
40
|
messages: [
|
|
26
41
|
...state.messages,
|
|
27
42
|
{
|
|
28
43
|
...message,
|
|
44
|
+
// 覆盖
|
|
45
|
+
createdAt: Date.now(),
|
|
29
46
|
updatedAt: Date.now(),
|
|
30
47
|
},
|
|
31
48
|
],
|
|
@@ -37,6 +54,7 @@ function createChatStore<Value, AIData>() {
|
|
|
37
54
|
if (m.uuid === message.uuid) {
|
|
38
55
|
return {
|
|
39
56
|
...message,
|
|
57
|
+
// 覆盖
|
|
40
58
|
updatedAt: Date.now(),
|
|
41
59
|
};
|
|
42
60
|
}
|
package/src/store/types.ts
CHANGED
|
@@ -9,6 +9,10 @@ enum EnumChatMessageStatus {
|
|
|
9
9
|
ERROR = 'error',
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
interface ChatMessageOfSystem {
|
|
13
|
+
data?: any;
|
|
14
|
+
}
|
|
15
|
+
|
|
12
16
|
interface ChatMessageOfUser {
|
|
13
17
|
text?: string;
|
|
14
18
|
files?: string[];
|
|
@@ -16,15 +20,24 @@ interface ChatMessageOfUser {
|
|
|
16
20
|
|
|
17
21
|
interface ChatMessageOfAI<AIData> {
|
|
18
22
|
data?: AIData;
|
|
23
|
+
/** 按需存取 */
|
|
24
|
+
session_id?: string;
|
|
19
25
|
}
|
|
20
26
|
|
|
21
27
|
interface ChatMessage<AIData> {
|
|
22
28
|
uuid: string;
|
|
23
|
-
|
|
24
|
-
type?: EnumChatMessageType;
|
|
29
|
+
|
|
25
30
|
status?: EnumChatMessageStatus;
|
|
31
|
+
|
|
32
|
+
type?: EnumChatMessageType;
|
|
33
|
+
system?: ChatMessageOfSystem;
|
|
26
34
|
user?: ChatMessageOfUser;
|
|
27
35
|
ai?: ChatMessageOfAI<AIData>;
|
|
36
|
+
|
|
37
|
+
/** 自动生成 */
|
|
38
|
+
createdAt?: number;
|
|
39
|
+
/** 自动更新 */
|
|
40
|
+
updatedAt?: number;
|
|
28
41
|
}
|
|
29
42
|
|
|
30
43
|
export { EnumChatMessageStatus, EnumChatMessageType };
|
package/src/style.scss
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
40% {
|
|
10
|
-
height:
|
|
11
|
-
box-shadow: 0 -
|
|
10
|
+
height: 8px;
|
|
11
|
+
box-shadow: 0 -8px theme('colors.white');
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
40% {
|
|
24
|
-
height:
|
|
25
|
-
box-shadow: 0 -
|
|
24
|
+
height: 8px;
|
|
25
|
+
box-shadow: 0 -8px theme('colors.primary');
|
|
26
26
|
}
|
|
27
27
|
}
|
package/src/svgs/keyboard.svg
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" fill="none" version="1.1" width="20.70703125" height="20.70703125" viewBox="0 0 20.70703125 20.70703125"><g><path d="M4.66015625,7.7204833984375L4.66015625,7.1204833984375Q4.66015625,7.0712376784375,4.6697636088,7.0229382184375Q4.679370968,6.9746387584375,4.698216479,6.9291416384375Q4.717061989,6.8836445484375,4.744421437,6.8426982584375Q4.77178088,6.8017519684375,4.80660285,6.7669299984375Q4.84142482,6.7321080284375,4.88237113,6.7047485854375Q4.9233174,6.6773891374375,4.96881449,6.6585436274375Q5.01431161,6.6396981164375,5.06261107,6.6300907572375Q5.11091053,6.6204833984375,5.16015625,6.6204833984375L5.76015625,6.6204833984375Q5.80940185,6.6204833984375,5.85770135,6.6300907572375Q5.9060007500000005,6.6396981164375,5.95149775,6.6585436274375Q5.9969949499999995,6.6773891374375,6.03794125,6.7047485854375Q6.07888745,6.7321080284375,6.11370955,6.7669299984375Q6.14853145,6.8017519684375,6.17589095,6.8426982784375Q6.20325025,6.8836445484375,6.2220958500000005,6.9291416384375Q6.24094145,6.9746387584375,6.2505488499999995,7.0229382184375Q6.26015615,7.0712376784375,6.26015625,7.1204833984375L6.26015625,7.7204833984375Q6.26015615,7.7697289984375,6.2505488499999995,7.8180284984375Q6.24094145,7.8663278984375005,6.2220958500000005,7.9118248984375Q6.20325025,7.9573220984374995,6.17589095,7.9982683984375Q6.14853145,8.0392145984375,6.11370955,8.0740366984375Q6.07888745,8.1088585984375,6.03794125,8.1362180984375Q5.9969949499999995,8.1635773984375,5.95149795,8.1824229984375Q5.90600085,8.2012685984375,5.85770135,8.2108759984375Q5.80940185,8.2204832984375,5.76015625,8.2204833984375L5.16015625,8.2204833984375Q5.11091053,8.2204832984375,5.06261107,8.2108759984375Q5.01431161,8.2012685984375,4.96881449,8.1824229984375Q4.9233174,8.1635773984375,4.88237111,8.1362180984375Q4.84142482,8.1088585984375,4.80660285,8.0740366984375Q4.77178088,8.0392145984375,4.744421437,7.9982683984375Q4.717061989,7.9573220984374995,4.698216479,7.9118250984375Q4.679370968,7.8663279984375,4.6697636088,7.8180284984375Q4.66015625,7.7697289984375,4.66015625,7.7204833984375ZM4.66015625,10.8207640984375L4.66015625,10.2207641984375Q4.66015625,10.1715182984375,4.6697636088,10.1232187984375Q4.679370968,10.0749189984375,4.698216479,10.0294217984375Q4.717061989,9.9839245984375,4.744421437,9.9429783984375Q4.77178088,9.9020323984375,4.80660285,9.8672103984375Q4.84142482,9.8323883984375,4.88237113,9.805029398437501Q4.9233174,9.7776698984375,4.96881449,9.7588243984375Q5.01431161,9.7399787984375,5.06261107,9.7303714984375Q5.11091053,9.7207641984375,5.16015625,9.7207641984375L5.76015625,9.7207641984375Q5.80940185,9.7207641984375,5.85770135,9.7303714984375Q5.9060007500000005,9.7399787984375,5.95149775,9.7588240984375Q5.9969949499999995,9.7776696984375,6.03794125,9.8050291984375Q6.07888745,9.8323883984375,6.11370955,9.8672103984375Q6.14853145,9.9020323984375,6.17589095,9.9429783984375Q6.20325025,9.9839245984375,6.2220958500000005,10.0294217984375Q6.24094145,10.0749189984375,6.2505488499999995,10.1232184984375Q6.26015615,10.1715182984375,6.26015625,10.2207641984375L6.26015625,10.8207640984375Q6.26015615,10.8700093984375,6.2505488499999995,10.9183086984375Q6.24094145,10.9666079984375,6.2220958500000005,11.0121054984375Q6.20325025,11.0576028984375,6.17589095,11.0985488984375Q6.14853145,11.1394953984375,6.11370955,11.1743173984375Q6.07888745,11.2091388984375,6.03794125,11.2364983984375Q5.9969949499999995,11.263857798437499,5.95149795,11.282702898437499Q5.90600085,11.3015489984375,5.85770135,11.3111562984375Q5.80940185,11.3207635984375,5.76015625,11.3207640984375L5.16015625,11.3207640984375Q5.11091053,11.3207635984375,5.06261107,11.3111562984375Q5.01431161,11.3015489984375,4.96881449,11.282702898437499Q4.9233174,11.263857798437499,4.88237111,11.2364983984375Q4.84142482,11.2091388984375,4.80660285,11.1743173984375Q4.77178088,11.1394953984375,4.744421437,11.0985488984375Q4.717061989,11.0576028984375,4.698216479,11.0121054984375Q4.679370968,10.9666079984375,4.6697636088,10.9183086984375Q4.66015625,10.8700093984375,4.66015625,10.8207640984375ZM7.92285155,7.7204833984375L7.92285155,7.1204833984375Q7.92285155,7.0712376784375,7.93245885,7.0229382184375Q7.9420661500000005,6.9746387584375,7.96091175,6.9291416384375Q7.97975735,6.8836445484375,8.00711655,6.8426982584375Q8.03447585,6.8017519684375,8.06929775,6.7669299984375Q8.10411975,6.7321080284375,8.14506575,6.7047485854375Q8.186012250000001,6.6773891374375,8.23150945,6.6585436274375Q8.27700665,6.6396981164375,8.32530615,6.6300907572375Q8.37360575,6.6204833984375,8.42285155,6.6204833984375L9.022851450000001,6.6204833984375Q9.072096850000001,6.6204833984375,9.120396150000001,6.6300907572375Q9.168695450000001,6.6396981164375,9.21419285,6.6585436274375Q9.25968985,6.6773891374375,9.30063625,6.7047485854375Q9.34158225,6.7321080284375,9.376404749999999,6.7669299984375Q9.41122625,6.8017519684375,9.43858625,6.8426982784375Q9.46594525,6.8836445484375,9.48479085,6.9291416384375Q9.50363635,6.9746387584375,9.51324365,7.0229382184375Q9.522850949999999,7.0712376784375,9.522851450000001,7.1204833984375L9.522851450000001,7.7204833984375Q9.522850949999999,7.7697289984375,9.51324365,7.8180284984375Q9.50363635,7.8663278984375005,9.48479085,7.9118248984375Q9.46594525,7.9573220984374995,9.43858575,7.9982683984375Q9.41122625,8.0392145984375,9.376404749999999,8.0740366984375Q9.34158225,8.1088585984375,9.30063625,8.1362180984375Q9.25968935,8.1635773984375,9.214192350000001,8.1824229984375Q9.168695450000001,8.2012685984375,9.120396150000001,8.2108759984375Q9.072096850000001,8.2204832984375,9.022851450000001,8.2204833984375L8.42285155,8.2204833984375Q8.37360575,8.2204832984375,8.32530615,8.2108759984375Q8.27700665,8.2012685984375,8.23150925,8.1824229984375Q8.186012250000001,8.1635773984375,8.14506605,8.1362180984375Q8.10411975,8.1088585984375,8.06929775,8.0740366984375Q8.03447585,8.0392145984375,8.00711655,7.9982683984375Q7.97975705,7.9573220984374995,7.9609115500000005,7.9118250984375Q7.9420661500000005,7.8663279984375,7.93245885,7.8180284984375Q7.92285155,7.7697289984375,7.92285155,7.7204833984375ZM7.92285155,10.8207640984375L7.92285155,10.2207641984375Q7.92285155,10.1715182984375,7.93245885,10.1232187984375Q7.9420661500000005,10.0749189984375,7.96091175,10.0294217984375Q7.97975735,9.9839245984375,8.00711655,9.9429783984375Q8.03447585,9.9020323984375,8.06929775,9.8672103984375Q8.10411975,9.8323883984375,8.14506575,9.805029398437501Q8.186012250000001,9.7776698984375,8.23150945,9.7588243984375Q8.27700665,9.7399787984375,8.32530615,9.7303714984375Q8.37360575,9.7207641984375,8.42285155,9.7207641984375L9.022851450000001,9.7207641984375Q9.072096850000001,9.7207641984375,9.120396150000001,9.7303714984375Q9.168695450000001,9.7399787984375,9.21419285,9.7588240984375Q9.25968985,9.7776696984375,9.30063625,9.8050291984375Q9.34158225,9.8323883984375,9.376404749999999,9.8672103984375Q9.41122625,9.9020323984375,9.43858625,9.9429783984375Q9.46594525,9.9839245984375,9.48479085,10.0294217984375Q9.50363635,10.0749189984375,9.51324365,10.1232184984375Q9.522850949999999,10.1715182984375,9.522851450000001,10.2207641984375L9.522851450000001,10.8207640984375Q9.522850949999999,10.8700093984375,9.51324365,10.9183086984375Q9.50363635,10.9666079984375,9.48479085,11.0121054984375Q9.46594525,11.0576028984375,9.43858575,11.0985488984375Q9.41122625,11.1394953984375,9.376404749999999,11.1743173984375Q9.34158225,11.2091388984375,9.30063625,11.2364983984375Q9.25968935,11.263857798437499,9.214192350000001,11.282702898437499Q9.168695450000001,11.3015489984375,9.120396150000001,11.3111562984375Q9.072096850000001,11.3207635984375,9.022851450000001,11.3207640984375L8.42285155,11.3207640984375Q8.37360575,11.3207635984375,8.32530615,11.3111562984375Q8.27700665,11.3015489984375,8.23150925,11.282702898437499Q8.186012250000001,11.263857798437499,8.14506605,11.2364983984375Q8.10411975,11.2091388984375,8.06929775,11.1743173984375Q8.03447585,11.1394953984375,8.00711655,11.0985488984375Q7.97975705,11.0576028984375,7.9609115500000005,11.0121054984375Q7.9420661500000005,10.9666079984375,7.93245885,10.9183086984375Q7.92285155,10.8700093984375,7.92285155,10.8207640984375ZM11.18554685,7.7204833984375L11.18554685,7.1204833984375Q11.18554685,7.0712376784375,11.19515415,7.0229382184375Q11.20476155,6.9746387584375,11.22360705,6.9291416384375Q11.24245265,6.8836445484375,11.269811650000001,6.8426982584375Q11.29717115,6.8017519684375,11.331993149999999,6.7669299984375Q11.36681505,6.7321080284375,11.40776155,6.7047485854375Q11.44870755,6.6773891374375,11.49420455,6.6585436274375Q11.539701449999999,6.6396981164375,11.58800125,6.6300907572375Q11.63630105,6.6204833984375,11.68554685,6.6204833984375L12.28554675,6.6204833984375Q12.33479215,6.6204833984375,12.38309195,6.6300907572375Q12.43139075,6.6396981164375,12.47688775,6.6585436274375Q12.52238465,6.6773891374375,12.56333165,6.7047485854375Q12.60427765,6.7321080284375,12.63910005,6.7669299984375Q12.67392155,6.8017519684375,12.70128055,6.8426982784375Q12.72864055,6.8836445484375,12.74748615,6.9291416384375Q12.76633265,6.9746387584375,12.77593995,7.0229382184375Q12.78554725,7.0712376784375,12.78554725,7.1204833984375L12.78554725,7.7204833984375Q12.78554725,7.7697289984375,12.77593995,7.8180284984375Q12.76633265,7.8663278984375005,12.74748615,7.9118248984375Q12.72864055,7.9573220984374995,12.70128155,7.9982683984375Q12.67392155,8.0392145984375,12.63910005,8.0740366984375Q12.60427765,8.1088585984375,12.56333165,8.1362180984375Q12.52238465,8.1635773984375,12.47688775,8.1824229984375Q12.43139075,8.2012685984375,12.38309095,8.2108759984375Q12.33479215,8.2204832984375,12.28554675,8.2204833984375L11.68554685,8.2204833984375Q11.63630105,8.2204832984375,11.58800125,8.2108759984375Q11.539701449999999,8.2012685984375,11.49420455,8.1824229984375Q11.44870755,8.1635773984375,11.407761050000001,8.1362180984375Q11.36681505,8.1088585984375,11.331993149999999,8.0740366984375Q11.29717115,8.0392145984375,11.269811650000001,7.9982683984375Q11.24245215,7.9573220984374995,11.22360655,7.9118250984375Q11.20476155,7.8663279984375,11.19515415,7.8180284984375Q11.18554685,7.7697289984375,11.18554685,7.7204833984375ZM14.44726565,7.7204833984375L14.44726565,7.1204833984375Q14.44726565,7.0712376784375,14.45687295,7.0229382184375Q14.46648025,6.9746387584375,14.48532485,6.9291416384375Q14.50417045,6.8836445484375,14.53153035,6.8426982584375Q14.55888935,6.8017519684375,14.59371185,6.7669299984375Q14.62853435,6.7321080284375,14.66948025,6.7047485854375Q14.71042625,6.6773891374375,14.75592325,6.6585436274375Q14.80142025,6.6396981164375,14.84972025,6.6300907572375Q14.89802025,6.6204833984375,14.94726525,6.6204833984375L15.54726625,6.6204833984375Q15.59651125,6.6204833984375,15.64480925,6.6300907572375Q15.69310825,6.6396981164375,15.73860525,6.6585436274375Q15.78410325,6.6773891374375,15.82505025,6.7047485854375Q15.86599625,6.7321080284375,15.90081925,6.7669299984375Q15.93564025,6.8017519684375,15.96299925,6.8426982784375Q15.99035825,6.8836445484375,16.00920525,6.9291416384375Q16.028049250000002,6.9746387584375,16.037657250000002,7.0229382184375Q16.047265250000002,7.0712376784375,16.04726625,7.1204833984375L16.04726625,7.7204833984375Q16.047265250000002,7.7697289984375,16.037657250000002,7.8180284984375Q16.028049250000002,7.8663278984375005,16.00920525,7.9118248984375Q15.99035825,7.9573220984374995,15.96299925,7.9982683984375Q15.93564025,8.0392145984375,15.90081925,8.0740366984375Q15.86599625,8.1088585984375,15.82504925,8.1362180984375Q15.78410325,8.1635773984375,15.73860725,8.1824229984375Q15.69310925,8.2012685984375,15.64481025,8.2108759984375Q15.59651125,8.2204832984375,15.54726625,8.2204833984375L14.94726525,8.2204833984375Q14.89802025,8.2204832984375,14.84972025,8.2108759984375Q14.80142025,8.2012685984375,14.75592325,8.1824229984375Q14.71042625,8.1635773984375,14.66947925,8.1362180984375Q14.62853435,8.1088585984375,14.59371185,8.0740366984375Q14.55888935,8.0392145984375,14.53153035,7.9982683984375Q14.50417045,7.9573220984374995,14.48532485,7.9118250984375Q14.46648025,7.8663279984375,14.45687295,7.8180284984375Q14.44726565,7.7697289984375,14.44726565,7.7204833984375ZM11.18554685,10.8207640984375L11.18554685,10.2207641984375Q11.18554685,10.1715182984375,11.19515415,10.1232187984375Q11.20476155,10.0749189984375,11.22360705,10.0294217984375Q11.24245265,9.9839245984375,11.269811650000001,9.9429783984375Q11.29717115,9.9020323984375,11.331993149999999,9.8672103984375Q11.36681505,9.8323883984375,11.40776155,9.805029398437501Q11.44870755,9.7776698984375,11.49420455,9.7588243984375Q11.539701449999999,9.7399787984375,11.58800125,9.7303714984375Q11.63630105,9.7207641984375,11.68554685,9.7207641984375L12.28554675,9.7207641984375Q12.33479215,9.7207641984375,12.38309195,9.7303714984375Q12.43139075,9.7399787984375,12.47688775,9.7588240984375Q12.52238465,9.7776696984375,12.56333165,9.8050291984375Q12.60427765,9.8323883984375,12.63910005,9.8672103984375Q12.67392155,9.9020323984375,12.70128055,9.9429783984375Q12.72864055,9.9839245984375,12.74748615,10.0294217984375Q12.76633265,10.0749189984375,12.77593995,10.1232184984375Q12.78554725,10.1715182984375,12.78554725,10.2207641984375L12.78554725,10.8207640984375Q12.78554725,10.8700093984375,12.77593995,10.9183086984375Q12.76633265,10.9666079984375,12.74748615,11.0121054984375Q12.72864055,11.0576028984375,12.70128155,11.0985488984375Q12.67392155,11.1394953984375,12.63910005,11.1743173984375Q12.60427765,11.2091388984375,12.56333165,11.2364983984375Q12.52238465,11.263857798437499,12.47688775,11.282702898437499Q12.43139075,11.3015489984375,12.38309095,11.3111562984375Q12.33479215,11.3207635984375,12.28554675,11.3207640984375L11.68554685,11.3207640984375Q11.63630105,11.3207635984375,11.58800125,11.3111562984375Q11.539701449999999,11.3015489984375,11.49420455,11.282702898437499Q11.44870755,11.263857798437499,11.407761050000001,11.2364983984375Q11.36681505,11.2091388984375,11.331993149999999,11.1743173984375Q11.29717115,11.1394953984375,11.269811650000001,11.0985488984375Q11.24245215,11.0576028984375,11.22360655,11.0121054984375Q11.20476155,10.9666079984375,11.19515415,10.9183086984375Q11.18554685,10.8700093984375,11.18554685,10.8207640984375ZM14.44726565,10.8207640984375L14.44726565,10.2207641984375Q14.44726565,10.1715182984375,14.45687295,10.1232187984375Q14.46648025,10.0749189984375,14.48532485,10.0294217984375Q14.50417045,9.9839245984375,14.53153035,9.9429783984375Q14.55888935,9.9020323984375,14.59371185,9.8672103984375Q14.62853435,9.8323883984375,14.66948025,9.805029398437501Q14.71042625,9.7776698984375,14.75592325,9.7588243984375Q14.80142025,9.7399787984375,14.84972025,9.7303714984375Q14.89802025,9.7207641984375,14.94726525,9.7207641984375L15.54726625,9.7207641984375Q15.59651125,9.7207641984375,15.64480925,9.7303714984375Q15.69310825,9.7399787984375,15.73860525,9.7588240984375Q15.78410325,9.7776696984375,15.82505025,9.8050291984375Q15.86599625,9.8323883984375,15.90081925,9.8672103984375Q15.93564025,9.9020323984375,15.96299925,9.9429783984375Q15.99035825,9.9839245984375,16.00920525,10.0294217984375Q16.028049250000002,10.0749189984375,16.037657250000002,10.1232184984375Q16.047265250000002,10.1715182984375,16.04726625,10.2207641984375L16.04726625,10.8207640984375Q16.047265250000002,10.8700093984375,16.037657250000002,10.9183086984375Q16.028049250000002,10.9666079984375,16.00920525,11.0121054984375Q15.99035825,11.0576028984375,15.96299925,11.0985488984375Q15.93564025,11.1394953984375,15.90081925,11.1743173984375Q15.86599625,11.2091388984375,15.82504925,11.2364983984375Q15.78410325,11.263857798437499,15.73860725,11.282702898437499Q15.69310925,11.3015489984375,15.64481025,11.3111562984375Q15.59651125,11.3207635984375,15.54726625,11.3207640984375L14.94726525,11.3207640984375Q14.89802025,11.3207635984375,14.84972025,11.3111562984375Q14.80142025,11.3015489984375,14.75592325,11.282702898437499Q14.71042625,11.263857798437499,14.66947925,11.2364983984375Q14.62853435,11.2091388984375,14.59371185,11.1743173984375Q14.55888935,11.1394953984375,14.53153035,11.0985488984375Q14.50417045,11.0576028984375,14.48532485,11.0121054984375Q14.46648025,10.9666079984375,14.45687295,10.9183086984375Q14.44726565,10.8700093984375,14.44726565,10.8207640984375Z" fill="#222222" fill-opacity="1"/></g><rect x="6.4091796875" y="13.35443115234375" width="7.887460708618164" height="1.399999976158142" rx="0.5" fill="#222222" fill-opacity="1"/><ellipse cx="10.353515625" cy="10.353515625" rx="9.228515625" ry="9.228515625" fill-opacity="0" stroke-opacity="1" stroke="#222222" fill="none" stroke-width="1.5" stroke-linecap="ROUND" stroke-linejoin="round"/></svg>
|
|
1
|
+
<svg width="20" height="18" viewBox="0 0 20 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M14.9561 0C15.6719 -6.75109e-08 16.2617 -0.000981836 16.7402 0.0380859C17.2288 0.0780087 17.6787 0.164078 18.1006 0.378906C18.7546 0.712247 19.2867 1.24447 19.6201 1.89844C19.8351 2.32031 19.921 2.77023 19.9609 3.25879C20 3.7373 19.999 4.32705 19.999 5.04297V12.3477C19.999 13.0634 20 13.6534 19.9609 14.1318C19.921 14.6203 19.8349 15.0694 19.6201 15.4912C19.2867 16.1456 18.7549 16.6782 18.1006 17.0117C17.6786 17.2267 17.2289 17.3126 16.7402 17.3525C16.2617 17.3916 15.672 17.3906 14.9561 17.3906H5.04297C4.32702 17.3906 3.73732 17.3916 3.25879 17.3525C2.77019 17.3126 2.32034 17.2267 1.89844 17.0117C1.24428 16.6782 0.712259 16.1455 0.378906 15.4912C0.1642 15.0695 0.078011 14.6202 0.0380859 14.1318C-0.000970309 13.6534 -2.36304e-07 13.0634 0 12.3477V5.04297C-4.75188e-07 4.32705 -0.00100976 3.7373 0.0380859 3.25879C0.0780378 2.7702 0.163945 2.32032 0.378906 1.89844C0.712319 1.24436 1.24435 0.712298 1.89844 0.378906C2.32035 0.163931 2.77017 0.0780345 3.25879 0.0380859C3.73732 -0.00101134 4.32702 -3.93286e-07 5.04297 0H14.9561ZM5.04297 1.73828C4.29836 1.73828 3.79169 1.73951 3.40039 1.77148C3.01944 1.80264 2.82442 1.85944 2.68848 1.92871C2.36139 2.09541 2.09543 2.3614 1.92871 2.68848C1.85945 2.82441 1.80264 3.0195 1.77148 3.40039C1.73951 3.79168 1.73828 4.29841 1.73828 5.04297V12.3477C1.73828 13.092 1.73955 13.599 1.77148 13.9902C1.8026 14.3707 1.85956 14.5662 1.92871 14.7021C2.09537 15.0291 2.36157 15.2952 2.68848 15.4619C2.82441 15.5312 3.01958 15.588 3.40039 15.6191C3.79169 15.6511 4.29836 15.6514 5.04297 15.6514H14.9561C15.7006 15.6514 16.2073 15.6511 16.5986 15.6191C16.9796 15.588 17.1746 15.5312 17.3105 15.4619C17.6377 15.2952 17.9036 15.0293 18.0703 14.7021C18.1395 14.5662 18.1964 14.371 18.2275 13.9902C18.2595 13.599 18.2607 13.092 18.2607 12.3477V5.04297C18.2607 4.29841 18.2595 3.79168 18.2275 3.40039C18.1964 3.01955 18.1396 2.82441 18.0703 2.68848C17.9036 2.36151 17.6376 2.09536 17.3105 1.92871C17.1746 1.85954 16.9793 1.8026 16.5986 1.77148C16.2073 1.73954 15.7006 1.73828 14.9561 1.73828H5.04297ZM14.7822 12.1738C15.2625 12.1738 15.6523 12.5637 15.6523 13.0439C15.6521 13.5239 15.2623 13.9131 14.7822 13.9131H5.2168C4.73692 13.9129 4.34795 13.5238 4.34766 13.0439C4.34766 12.5638 4.73674 12.174 5.2168 12.1738H14.7822ZM6.08691 7.82617C6.56708 7.82626 6.95605 8.2161 6.95605 8.69629V9.56543C6.95592 10.0455 6.567 10.4345 6.08691 10.4346H5.2168C4.73683 10.4344 4.3478 10.0454 4.34766 9.56543V8.69629C4.34766 8.21618 4.73674 7.82639 5.2168 7.82617H6.08691ZM10.4346 7.82617C10.9148 7.82618 11.3047 8.21605 11.3047 8.69629V9.56543C11.3045 10.0456 10.9147 10.4346 10.4346 10.4346H9.56543C9.08527 10.4346 8.69545 10.0456 8.69531 9.56543V8.69629C8.69531 8.21605 9.08519 7.82617 9.56543 7.82617H10.4346ZM14.7822 7.82617C15.2624 7.82627 15.6514 8.21611 15.6514 8.69629V9.56543C15.6512 10.0455 15.2623 10.4345 14.7822 10.4346H13.9121C13.4321 10.4344 13.0431 10.0454 13.043 9.56543V8.69629C13.043 8.21618 13.432 7.82638 13.9121 7.82617H14.7822ZM6.08691 3.47852C6.56695 3.4786 6.95583 3.86766 6.95605 4.34766V5.21777C6.95585 5.69779 6.56696 6.08683 6.08691 6.08691H5.2168C4.73687 6.08669 4.34787 5.6977 4.34766 5.21777V4.34766C4.34788 3.86774 4.73688 3.47874 5.2168 3.47852H6.08691ZM10.4346 3.47852C10.9147 3.47852 11.3045 3.86761 11.3047 4.34766V5.21777C11.3045 5.69784 10.9147 6.08691 10.4346 6.08691H9.56543C9.08532 6.08691 8.69552 5.69784 8.69531 5.21777V4.34766C8.69554 3.86761 9.08533 3.47852 9.56543 3.47852H10.4346ZM14.7822 3.47852C15.2622 3.47861 15.6511 3.86767 15.6514 4.34766V5.21777C15.6512 5.69778 15.2623 6.08682 14.7822 6.08691H13.9121C13.4322 6.0867 13.0432 5.69771 13.043 5.21777V4.34766C13.0432 3.86774 13.4322 3.47872 13.9121 3.47852H14.7822Z"/>
|
|
3
|
+
</svg>
|
package/src/svgs/record.svg
CHANGED
|
@@ -1 +1,3 @@
|
|
|
1
|
-
<svg
|
|
1
|
+
<svg width="14" height="18" viewBox="0 0 14 18" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M1.46387 8.41895C1.46399 11.2488 3.75802 13.5429 6.58789 13.543C9.41782 13.543 11.7118 11.2488 11.7119 8.41895H13.1758C13.1757 11.8102 10.6136 14.6009 7.31934 14.9648V17.2031H5.85547V14.9648C2.56181 14.6004 0.000112876 11.8097 0 8.41895H1.46387ZM6.58789 0C8.60932 0 10.248 1.63873 10.248 3.66016V8.41797C10.248 10.4394 8.60932 12.0781 6.58789 12.0781C4.56664 12.0779 2.92773 10.4393 2.92773 8.41797V3.66016C2.92775 1.63887 4.56665 0.000220666 6.58789 0Z" />
|
|
3
|
+
</svg>
|