@asgard-js/react 0.0.43-canary.25 → 0.0.43-canary.4
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/dist/components/chatbot/api-key-input/api-key-input.d.ts.map +1 -1
- package/dist/components/chatbot/chatbot.d.ts +2 -1
- package/dist/components/chatbot/chatbot.d.ts.map +1 -1
- package/dist/context/asgard-theme-context.d.ts +0 -2
- package/dist/context/asgard-theme-context.d.ts.map +1 -1
- package/dist/index.js +19532 -21354
- package/dist/style.css +1 -1
- package/package.json +2 -2
- package/src/components/.DS_Store +0 -0
- package/src/components/chatbot/api-key-input/api-key-input.module.scss +71 -43
- package/src/components/chatbot/api-key-input/api-key-input.tsx +40 -28
- package/src/components/chatbot/chatbot.tsx +28 -22
- package/src/context/asgard-theme-context.tsx +0 -7
- package/src/index.ts +1 -1
- package/src/components/chatbot/chatbot.module.scss +0 -24
- package/src/icons/eye-off.svg +0 -4
- package/src/icons/eye.svg +0 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asgard-js/react",
|
|
3
|
-
"version": "0.0.43-canary.
|
|
3
|
+
"version": "0.0.43-canary.4",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -54,7 +54,7 @@
|
|
|
54
54
|
"vitest": "^1.6.0"
|
|
55
55
|
},
|
|
56
56
|
"peerDependencies": {
|
|
57
|
-
"@asgard-js/core": "^0.0.
|
|
57
|
+
"@asgard-js/core": "^0.0.42-canary.1",
|
|
58
58
|
"react": "^18.0.0",
|
|
59
59
|
"react-dom": "^18.0.0"
|
|
60
60
|
},
|
package/src/components/.DS_Store
CHANGED
|
Binary file
|
|
@@ -1,61 +1,53 @@
|
|
|
1
|
-
.
|
|
2
|
-
width:
|
|
3
|
-
max-width: calc(100vw - 32px);
|
|
1
|
+
.container {
|
|
2
|
+
width: 220px;
|
|
4
3
|
background-color: var(--asg-color-bg);
|
|
5
4
|
border-radius: 12px;
|
|
6
|
-
padding:
|
|
5
|
+
padding: 12px 20px;
|
|
7
6
|
border: 0.5px solid var(--asg-color-border);
|
|
8
|
-
|
|
9
|
-
@media (max-width: 480px) {
|
|
10
|
-
width: 100%;
|
|
11
|
-
max-width: none;
|
|
12
|
-
padding: 20px;
|
|
13
|
-
margin: 0 16px;
|
|
14
|
-
}
|
|
15
7
|
}
|
|
16
8
|
|
|
17
|
-
.
|
|
9
|
+
.header {
|
|
18
10
|
display: flex;
|
|
19
11
|
flex-direction: column;
|
|
20
|
-
gap:
|
|
12
|
+
gap: 4px;
|
|
21
13
|
align-items: center;
|
|
22
|
-
margin-bottom:
|
|
14
|
+
margin-bottom: 12px;
|
|
23
15
|
}
|
|
24
16
|
|
|
25
|
-
.
|
|
26
|
-
width:
|
|
27
|
-
height:
|
|
17
|
+
.icon {
|
|
18
|
+
width: 24px;
|
|
19
|
+
height: 24px;
|
|
28
20
|
}
|
|
29
21
|
|
|
30
|
-
.
|
|
22
|
+
.title {
|
|
31
23
|
margin: 0;
|
|
32
|
-
font-size:
|
|
24
|
+
font-size: 18px;
|
|
33
25
|
font-weight: 500;
|
|
34
26
|
color: white;
|
|
35
27
|
}
|
|
36
28
|
|
|
37
|
-
.
|
|
29
|
+
.form {
|
|
38
30
|
width: 100%;
|
|
39
31
|
display: flex;
|
|
40
32
|
flex-direction: column;
|
|
41
33
|
gap: 20px;
|
|
42
34
|
}
|
|
43
35
|
|
|
44
|
-
.
|
|
36
|
+
.label {
|
|
45
37
|
display: block;
|
|
46
38
|
font-size: 14px;
|
|
47
39
|
color: rgba(255, 255, 255, 0.7);
|
|
48
40
|
margin-bottom: 8px;
|
|
49
41
|
}
|
|
50
42
|
|
|
51
|
-
.
|
|
43
|
+
.inputWrapper {
|
|
52
44
|
position: relative;
|
|
53
45
|
width: 100%;
|
|
54
46
|
}
|
|
55
47
|
|
|
56
|
-
.
|
|
48
|
+
.input {
|
|
57
49
|
width: 100%;
|
|
58
|
-
height:
|
|
50
|
+
height: 42px;
|
|
59
51
|
padding: 0 40px 0 12px;
|
|
60
52
|
font-size: 14px;
|
|
61
53
|
font-weight: 400;
|
|
@@ -67,11 +59,6 @@
|
|
|
67
59
|
transition: all 0.2s ease;
|
|
68
60
|
box-sizing: border-box;
|
|
69
61
|
|
|
70
|
-
@media (max-width: 480px) {
|
|
71
|
-
height: 44px;
|
|
72
|
-
font-size: 16px;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
62
|
&::placeholder {
|
|
76
63
|
color: rgba(255, 255, 255, 0.4);
|
|
77
64
|
}
|
|
@@ -81,18 +68,18 @@
|
|
|
81
68
|
background: rgba(255, 255, 255, 0.08);
|
|
82
69
|
}
|
|
83
70
|
|
|
84
|
-
|
|
85
|
-
border-color:
|
|
86
|
-
background: rgba(255,
|
|
71
|
+
&.error {
|
|
72
|
+
border-color: rgba(255, 69, 58, 0.6);
|
|
73
|
+
background: rgba(255, 69, 58, 0.05);
|
|
87
74
|
}
|
|
88
75
|
|
|
89
|
-
|
|
76
|
+
&.disabled {
|
|
90
77
|
opacity: 0.5;
|
|
91
78
|
cursor: not-allowed;
|
|
92
79
|
}
|
|
93
80
|
}
|
|
94
81
|
|
|
95
|
-
.
|
|
82
|
+
.toggleButton {
|
|
96
83
|
position: absolute;
|
|
97
84
|
right: 8px;
|
|
98
85
|
top: 50%;
|
|
@@ -119,23 +106,23 @@
|
|
|
119
106
|
}
|
|
120
107
|
}
|
|
121
108
|
|
|
122
|
-
.
|
|
109
|
+
.toggleIcon {
|
|
123
110
|
width: 16px;
|
|
124
111
|
height: 16px;
|
|
125
112
|
}
|
|
126
113
|
|
|
127
|
-
.
|
|
114
|
+
.errorMessage {
|
|
128
115
|
margin-top: 6px;
|
|
129
116
|
font-size: 12px;
|
|
130
|
-
color:
|
|
117
|
+
color: rgba(255, 69, 58, 0.8);
|
|
131
118
|
}
|
|
132
119
|
|
|
133
|
-
.
|
|
120
|
+
.submitButton {
|
|
134
121
|
width: 100%;
|
|
135
|
-
height:
|
|
122
|
+
height: 42px;
|
|
123
|
+
background: #5856d6;
|
|
136
124
|
border: none;
|
|
137
125
|
border-radius: 6px;
|
|
138
|
-
background-color: #5856d6;
|
|
139
126
|
color: white;
|
|
140
127
|
font-size: 14px;
|
|
141
128
|
font-weight: 500;
|
|
@@ -143,9 +130,13 @@
|
|
|
143
130
|
transition: all 0.2s ease;
|
|
144
131
|
outline: none;
|
|
145
132
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
133
|
+
&:hover:not(:disabled) {
|
|
134
|
+
background: #4845c7;
|
|
135
|
+
transform: translateY(-1px);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
&:active:not(:disabled) {
|
|
139
|
+
transform: translateY(0);
|
|
149
140
|
}
|
|
150
141
|
|
|
151
142
|
&:disabled {
|
|
@@ -153,4 +144,41 @@
|
|
|
153
144
|
cursor: not-allowed;
|
|
154
145
|
transform: none;
|
|
155
146
|
}
|
|
147
|
+
|
|
148
|
+
&.loading {
|
|
149
|
+
position: relative;
|
|
150
|
+
color: transparent;
|
|
151
|
+
|
|
152
|
+
&::after {
|
|
153
|
+
content: '';
|
|
154
|
+
position: absolute;
|
|
155
|
+
top: 50%;
|
|
156
|
+
left: 50%;
|
|
157
|
+
width: 16px;
|
|
158
|
+
height: 16px;
|
|
159
|
+
margin-left: -8px;
|
|
160
|
+
margin-top: -8px;
|
|
161
|
+
border: 2px solid transparent;
|
|
162
|
+
border-top: 2px solid white;
|
|
163
|
+
border-radius: 50%;
|
|
164
|
+
animation: spin 1s linear infinite;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
@keyframes spin {
|
|
170
|
+
0% {
|
|
171
|
+
transform: rotate(0deg);
|
|
172
|
+
}
|
|
173
|
+
100% {
|
|
174
|
+
transform: rotate(360deg);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// 響應式設計
|
|
179
|
+
@media (max-width: 280px) {
|
|
180
|
+
.container {
|
|
181
|
+
width: 100%;
|
|
182
|
+
min-width: 200px;
|
|
183
|
+
}
|
|
156
184
|
}
|
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { useState, FormEvent, ChangeEvent } from 'react';
|
|
2
2
|
import clsx from 'clsx';
|
|
3
|
+
import ProfileSvg from '../../../icons/profile.svg?react';
|
|
3
4
|
import { useAsgardThemeContext } from '../../../context/asgard-theme-context';
|
|
4
|
-
import { useAsgardContext } from '../../../context/asgard-service-context';
|
|
5
|
-
import { ProfileIcon } from '../profile-icon';
|
|
6
|
-
import EyeSvg from '../../../icons/eye.svg?react';
|
|
7
|
-
import EyeOffSvg from '../../../icons/eye-off.svg?react';
|
|
8
5
|
import styles from './api-key-input.module.scss';
|
|
9
6
|
|
|
10
7
|
export interface ApiKeyInputProps {
|
|
@@ -29,7 +26,6 @@ export function ApiKeyInput({
|
|
|
29
26
|
const [apiKey, setApiKey] = useState('');
|
|
30
27
|
const [showPassword, setShowPassword] = useState(false);
|
|
31
28
|
const { chatbot } = useAsgardThemeContext();
|
|
32
|
-
const { avatar } = useAsgardContext();
|
|
33
29
|
|
|
34
30
|
const handleSubmit = (e: FormEvent): void => {
|
|
35
31
|
e.preventDefault();
|
|
@@ -48,29 +44,29 @@ export function ApiKeyInput({
|
|
|
48
44
|
|
|
49
45
|
return (
|
|
50
46
|
<div
|
|
51
|
-
className={clsx(styles.
|
|
47
|
+
className={clsx(styles.container, className)}
|
|
52
48
|
style={{
|
|
53
49
|
backgroundColor: chatbot.backgroundColor,
|
|
54
50
|
borderColor: chatbot.borderColor,
|
|
55
51
|
}}
|
|
56
52
|
>
|
|
57
|
-
<div className={styles.
|
|
58
|
-
<
|
|
59
|
-
<h2 className={styles.
|
|
53
|
+
<div className={styles.header}>
|
|
54
|
+
<ProfileSvg className={styles.icon} />
|
|
55
|
+
<h2 className={styles.title}>{title}</h2>
|
|
60
56
|
</div>
|
|
61
57
|
|
|
62
|
-
<form onSubmit={handleSubmit} className={styles.
|
|
63
|
-
<div>
|
|
64
|
-
<label className={styles.
|
|
65
|
-
<div className={styles.
|
|
58
|
+
<form onSubmit={handleSubmit} className={styles.form}>
|
|
59
|
+
<div className={styles.inputGroup}>
|
|
60
|
+
<label className={styles.label}>Key</label>
|
|
61
|
+
<div className={styles.inputWrapper}>
|
|
66
62
|
<input
|
|
67
63
|
type={showPassword ? 'text' : 'password'}
|
|
68
64
|
value={apiKey}
|
|
69
65
|
onChange={handleInputChange}
|
|
70
66
|
placeholder={placeholder}
|
|
71
|
-
className={clsx(styles.
|
|
72
|
-
[styles
|
|
73
|
-
[styles
|
|
67
|
+
className={clsx(styles.input, {
|
|
68
|
+
[styles.error]: error,
|
|
69
|
+
[styles.disabled]: loading,
|
|
74
70
|
})}
|
|
75
71
|
disabled={loading}
|
|
76
72
|
autoComplete="off"
|
|
@@ -79,29 +75,45 @@ export function ApiKeyInput({
|
|
|
79
75
|
<button
|
|
80
76
|
type="button"
|
|
81
77
|
onClick={togglePasswordVisibility}
|
|
82
|
-
className={styles.
|
|
78
|
+
className={styles.toggleButton}
|
|
83
79
|
disabled={loading}
|
|
84
80
|
aria-label={showPassword ? 'Hide password' : 'Show password'}
|
|
85
81
|
>
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
82
|
+
<svg
|
|
83
|
+
className={styles.toggleIcon}
|
|
84
|
+
width="16"
|
|
85
|
+
height="16"
|
|
86
|
+
viewBox="0 0 24 24"
|
|
87
|
+
fill="none"
|
|
88
|
+
stroke="currentColor"
|
|
89
|
+
strokeWidth="2"
|
|
90
|
+
strokeLinecap="round"
|
|
91
|
+
strokeLinejoin="round"
|
|
92
|
+
>
|
|
93
|
+
{showPassword ? (
|
|
94
|
+
<>
|
|
95
|
+
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24" />
|
|
96
|
+
<line x1="1" y1="1" x2="23" y2="23" />
|
|
97
|
+
</>
|
|
98
|
+
) : (
|
|
99
|
+
<>
|
|
100
|
+
<path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
|
|
101
|
+
<circle cx="12" cy="12" r="3" />
|
|
102
|
+
</>
|
|
103
|
+
)}
|
|
104
|
+
</svg>
|
|
91
105
|
</button>
|
|
92
106
|
)}
|
|
93
107
|
</div>
|
|
94
|
-
{error && <div className={styles.
|
|
108
|
+
{error && <div className={styles.errorMessage}>{error}</div>}
|
|
95
109
|
</div>
|
|
96
110
|
|
|
97
111
|
<button
|
|
98
112
|
type="submit"
|
|
99
113
|
disabled={!apiKey.trim() || loading}
|
|
100
|
-
className={styles.
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
color: chatbot?.secondaryColor,
|
|
104
|
-
}}
|
|
114
|
+
className={clsx(styles.submitButton, {
|
|
115
|
+
[styles.loading]: loading,
|
|
116
|
+
})}
|
|
105
117
|
>
|
|
106
118
|
{loading ? 'Loading...' : 'Continue'}
|
|
107
119
|
</button>
|
|
@@ -17,14 +17,13 @@ import {
|
|
|
17
17
|
AsgardAppInitializationContextProvider,
|
|
18
18
|
AsgardServiceContextProviderProps,
|
|
19
19
|
} from '../../context';
|
|
20
|
-
import { AuthState } from '@asgard-js/core';
|
|
21
|
-
import clsx from 'clsx';
|
|
22
20
|
import { ApiKeyInput } from './api-key-input';
|
|
23
21
|
import { ChatbotHeader } from './chatbot-header';
|
|
24
22
|
import { ChatbotBody } from './chatbot-body';
|
|
25
23
|
import { ChatbotFooter } from './chatbot-footer';
|
|
26
24
|
import { ChatbotContainer } from './chatbot-container/chatbot-container';
|
|
27
|
-
|
|
25
|
+
|
|
26
|
+
type AuthState = 'loading' | 'needApiKey' | 'authenticated' | 'error';
|
|
28
27
|
|
|
29
28
|
interface ChatbotProps extends AsgardTemplateContextValue {
|
|
30
29
|
className?: string;
|
|
@@ -96,40 +95,47 @@ export const Chatbot = forwardRef(function Chatbot(
|
|
|
96
95
|
switch (authState) {
|
|
97
96
|
case 'loading':
|
|
98
97
|
return (
|
|
99
|
-
<div
|
|
98
|
+
<div style={{
|
|
99
|
+
display: 'flex',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
justifyContent: 'center',
|
|
102
|
+
flex: 1,
|
|
103
|
+
padding: '20px'
|
|
104
|
+
}}>
|
|
100
105
|
{loadingComponent || <div>Loading...</div>}
|
|
101
106
|
</div>
|
|
102
107
|
);
|
|
103
108
|
|
|
104
109
|
case 'needApiKey':
|
|
105
110
|
return (
|
|
106
|
-
<div
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
case 'invalidApiKey':
|
|
116
|
-
return (
|
|
117
|
-
<div className={styles.chatbot__auth_state_container}>
|
|
111
|
+
<div style={{
|
|
112
|
+
display: 'flex',
|
|
113
|
+
alignItems: 'center',
|
|
114
|
+
justifyContent: 'center',
|
|
115
|
+
flex: 1,
|
|
116
|
+
padding: '20px'
|
|
117
|
+
}}>
|
|
118
118
|
<ApiKeyInput
|
|
119
119
|
title={title}
|
|
120
120
|
onSubmit={onApiKeySubmit || (() => {})}
|
|
121
121
|
placeholder="Enter your key"
|
|
122
|
-
error="Please check if the key is correct."
|
|
123
122
|
/>
|
|
124
123
|
</div>
|
|
125
124
|
);
|
|
126
125
|
|
|
127
126
|
case 'error':
|
|
128
127
|
return (
|
|
129
|
-
<div
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
128
|
+
<div style={{
|
|
129
|
+
display: 'flex',
|
|
130
|
+
alignItems: 'center',
|
|
131
|
+
justifyContent: 'center',
|
|
132
|
+
flex: 1,
|
|
133
|
+
padding: '20px',
|
|
134
|
+
color: '#ff6b6b'
|
|
135
|
+
}}>
|
|
136
|
+
<div style={{ textAlign: 'center' }}>
|
|
137
|
+
<div style={{ fontSize: '16px', marginBottom: '8px' }}>⚠️</div>
|
|
138
|
+
<div>Something went wrong. Please try again later.</div>
|
|
133
139
|
</div>
|
|
134
140
|
</div>
|
|
135
141
|
);
|
|
@@ -153,7 +159,7 @@ export const Chatbot = forwardRef(function Chatbot(
|
|
|
153
159
|
};
|
|
154
160
|
|
|
155
161
|
// Don't initialize SSE connection when explicitly needing API key or in error state
|
|
156
|
-
if (authState !== 'needApiKey' && authState !== 'error'
|
|
162
|
+
if (authState !== 'needApiKey' && authState !== 'error') {
|
|
157
163
|
return (
|
|
158
164
|
<AsgardAppInitializationContextProvider
|
|
159
165
|
enabled={enableLoadConfigFromService}
|
|
@@ -31,8 +31,6 @@ export interface AsgardThemeContextValue {
|
|
|
31
31
|
backgroundColor?: CSSProperties['backgroundColor'];
|
|
32
32
|
borderColor?: CSSProperties['borderColor'];
|
|
33
33
|
inactiveColor?: CSSProperties['color'];
|
|
34
|
-
mainColor?: CSSProperties['color'];
|
|
35
|
-
secondaryColor?: CSSProperties['color'];
|
|
36
34
|
primaryComponent?: {
|
|
37
35
|
mainColor?: CSSProperties['color'];
|
|
38
36
|
secondaryColor?: CSSProperties['color'];
|
|
@@ -269,9 +267,6 @@ export function AsgardThemeContextProvider(
|
|
|
269
267
|
chatbot: {
|
|
270
268
|
backgroundColor: themeFromAnnotations.chatbot?.backgroundColor,
|
|
271
269
|
borderColor: themeFromAnnotations.chatbot?.borderColor,
|
|
272
|
-
mainColor: themeFromAnnotations.chatbot?.primaryComponent?.mainColor,
|
|
273
|
-
secondaryColor: themeFromAnnotations.chatbot?.primaryComponent?.secondaryColor,
|
|
274
|
-
|
|
275
270
|
header: {
|
|
276
271
|
style: {
|
|
277
272
|
borderBottomColor: themeFromAnnotations.chatbot?.borderColor,
|
|
@@ -412,8 +407,6 @@ export function AsgardThemeContextProvider(
|
|
|
412
407
|
chatbot: {
|
|
413
408
|
backgroundColor: propsTheme.chatbot?.backgroundColor,
|
|
414
409
|
borderColor: propsTheme.chatbot?.borderColor,
|
|
415
|
-
mainColor: propsTheme.chatbot?.primaryComponent?.mainColor,
|
|
416
|
-
secondaryColor: propsTheme.chatbot?.primaryComponent?.secondaryColor,
|
|
417
410
|
header: {
|
|
418
411
|
style: {
|
|
419
412
|
borderBottomColor: propsTheme.chatbot?.borderColor,
|
package/src/index.ts
CHANGED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
.chatbot__auth_state_container {
|
|
2
|
-
display: flex;
|
|
3
|
-
align-items: center;
|
|
4
|
-
justify-content: center;
|
|
5
|
-
flex: 1;
|
|
6
|
-
padding: 20px;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
.chatbot__error_state {
|
|
10
|
-
color: #ff6b6b;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
.chatbot__error_state__content {
|
|
14
|
-
text-align: center;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
.chatbot__error_state__icon {
|
|
18
|
-
font-size: 16px;
|
|
19
|
-
margin-bottom: 8px;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
.chatbot__error_state__message {
|
|
23
|
-
// No additional styles needed - uses default text styling
|
|
24
|
-
}
|
package/src/icons/eye-off.svg
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2
|
-
<path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"/>
|
|
3
|
-
<line x1="1" y1="1" x2="23" y2="23"/>
|
|
4
|
-
</svg>
|
package/src/icons/eye.svg
DELETED