@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asgard-js/react",
3
- "version": "0.0.43-canary.25",
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.43-canary.25",
57
+ "@asgard-js/core": "^0.0.42-canary.1",
58
58
  "react": "^18.0.0",
59
59
  "react-dom": "^18.0.0"
60
60
  },
Binary file
@@ -1,61 +1,53 @@
1
- .api_key_input {
2
- width: 360px;
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: 24px;
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
- .api_key_input__header {
9
+ .header {
18
10
  display: flex;
19
11
  flex-direction: column;
20
- gap: 8px;
12
+ gap: 4px;
21
13
  align-items: center;
22
- margin-bottom: 20px;
14
+ margin-bottom: 12px;
23
15
  }
24
16
 
25
- .api_key_input__icon {
26
- width: 40px;
27
- height: 40px;
17
+ .icon {
18
+ width: 24px;
19
+ height: 24px;
28
20
  }
29
21
 
30
- .api_key_input__title {
22
+ .title {
31
23
  margin: 0;
32
- font-size: 20px;
24
+ font-size: 18px;
33
25
  font-weight: 500;
34
26
  color: white;
35
27
  }
36
28
 
37
- .api_key_input__form {
29
+ .form {
38
30
  width: 100%;
39
31
  display: flex;
40
32
  flex-direction: column;
41
33
  gap: 20px;
42
34
  }
43
35
 
44
- .api_key_input__label {
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
- .api_key_input__input_wrapper {
43
+ .inputWrapper {
52
44
  position: relative;
53
45
  width: 100%;
54
46
  }
55
47
 
56
- .api_key_input__input {
48
+ .input {
57
49
  width: 100%;
58
- height: 48px;
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
- &--error {
85
- border-color: #FF4D4F;
86
- background: rgba(255, 77, 79, 0.05);
71
+ &.error {
72
+ border-color: rgba(255, 69, 58, 0.6);
73
+ background: rgba(255, 69, 58, 0.05);
87
74
  }
88
75
 
89
- &--disabled {
76
+ &.disabled {
90
77
  opacity: 0.5;
91
78
  cursor: not-allowed;
92
79
  }
93
80
  }
94
81
 
95
- .api_key_input__toggle_button {
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
- .api_key_input__toggle_icon {
109
+ .toggleIcon {
123
110
  width: 16px;
124
111
  height: 16px;
125
112
  }
126
113
 
127
- .api_key_input__error_message {
114
+ .errorMessage {
128
115
  margin-top: 6px;
129
116
  font-size: 12px;
130
- color: #FF4D4F;
117
+ color: rgba(255, 69, 58, 0.8);
131
118
  }
132
119
 
133
- .api_key_input__submit_button {
120
+ .submitButton {
134
121
  width: 100%;
135
- height: 48px;
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
- @media (max-width: 480px) {
147
- height: 44px;
148
- font-size: 16px;
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.api_key_input, className)}
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.api_key_input__header}>
58
- <ProfileIcon avatar={avatar} />
59
- <h2 className={styles.api_key_input__title} style={chatbot?.header?.title?.style}>{title}</h2>
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.api_key_input__form}>
63
- <div>
64
- <label className={styles.api_key_input__label}>Key</label>
65
- <div className={styles.api_key_input__input_wrapper}>
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.api_key_input__input, {
72
- [styles['api_key_input__input--error']]: error,
73
- [styles['api_key_input__input--disabled']]: loading,
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.api_key_input__toggle_button}
78
+ className={styles.toggleButton}
83
79
  disabled={loading}
84
80
  aria-label={showPassword ? 'Hide password' : 'Show password'}
85
81
  >
86
- {showPassword ? (
87
- <EyeOffSvg className={styles.api_key_input__toggle_icon} />
88
- ) : (
89
- <EyeSvg className={styles.api_key_input__toggle_icon} />
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.api_key_input__error_message}>{error}</div>}
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.api_key_input__submit_button}
101
- style={{
102
- backgroundColor: chatbot?.mainColor,
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
- import styles from './chatbot.module.scss';
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 className={styles.chatbot__auth_state_container}>
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 className={styles.chatbot__auth_state_container}>
107
- <ApiKeyInput
108
- title={title}
109
- onSubmit={onApiKeySubmit || (() => {})}
110
- placeholder="Enter your key"
111
- />
112
- </div>
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 className={clsx(styles.chatbot__auth_state_container, styles.chatbot__error_state)}>
130
- <div className={styles.chatbot__error_state__content}>
131
- <div className={styles.chatbot__error_state__icon}>⚠️</div>
132
- <div className={styles.chatbot__error_state__message}>Something went wrong. Please try again later.</div>
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' && authState !== 'invalidApiKey') {
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,4 +1,4 @@
1
1
  import 'highlight.js/styles/atom-one-dark.css';
2
2
  export * from './components';
3
3
  export * from './context';
4
- export * from './hooks';
4
+ export * from './hooks';
@@ -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
- }
@@ -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
@@ -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="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/>
3
- <circle cx="12" cy="12" r="3"/>
4
- </svg>