@asgard-js/react 0.0.43-canary.25 → 0.0.43-canary.3

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.3",
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,33 @@
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: 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
- }
16
-
17
- .api_key_input__header {
18
- display: flex;
19
- flex-direction: column;
20
- gap: 8px;
21
- align-items: center;
22
- margin-bottom: 20px;
23
7
  }
24
8
 
25
- .api_key_input__icon {
26
- width: 40px;
27
- height: 40px;
28
- }
29
-
30
- .api_key_input__title {
31
- margin: 0;
32
- font-size: 20px;
33
- font-weight: 500;
34
- color: white;
35
- }
36
-
37
- .api_key_input__form {
9
+ .form {
38
10
  width: 100%;
39
11
  display: flex;
40
12
  flex-direction: column;
41
13
  gap: 20px;
42
14
  }
43
15
 
44
- .api_key_input__label {
16
+ .label {
45
17
  display: block;
46
18
  font-size: 14px;
47
19
  color: rgba(255, 255, 255, 0.7);
48
20
  margin-bottom: 8px;
49
21
  }
50
22
 
51
- .api_key_input__input_wrapper {
23
+ .inputWrapper {
52
24
  position: relative;
53
25
  width: 100%;
54
26
  }
55
27
 
56
- .api_key_input__input {
28
+ .input {
57
29
  width: 100%;
58
- height: 48px;
30
+ height: 42px;
59
31
  padding: 0 40px 0 12px;
60
32
  font-size: 14px;
61
33
  font-weight: 400;
@@ -67,11 +39,6 @@
67
39
  transition: all 0.2s ease;
68
40
  box-sizing: border-box;
69
41
 
70
- @media (max-width: 480px) {
71
- height: 44px;
72
- font-size: 16px;
73
- }
74
-
75
42
  &::placeholder {
76
43
  color: rgba(255, 255, 255, 0.4);
77
44
  }
@@ -81,18 +48,18 @@
81
48
  background: rgba(255, 255, 255, 0.08);
82
49
  }
83
50
 
84
- &--error {
85
- border-color: #FF4D4F;
86
- background: rgba(255, 77, 79, 0.05);
51
+ &.error {
52
+ border-color: rgba(255, 69, 58, 0.6);
53
+ background: rgba(255, 69, 58, 0.05);
87
54
  }
88
55
 
89
- &--disabled {
56
+ &.disabled {
90
57
  opacity: 0.5;
91
58
  cursor: not-allowed;
92
59
  }
93
60
  }
94
61
 
95
- .api_key_input__toggle_button {
62
+ .toggleButton {
96
63
  position: absolute;
97
64
  right: 8px;
98
65
  top: 50%;
@@ -119,23 +86,23 @@
119
86
  }
120
87
  }
121
88
 
122
- .api_key_input__toggle_icon {
89
+ .toggleIcon {
123
90
  width: 16px;
124
91
  height: 16px;
125
92
  }
126
93
 
127
- .api_key_input__error_message {
94
+ .errorMessage {
128
95
  margin-top: 6px;
129
96
  font-size: 12px;
130
- color: #FF4D4F;
97
+ color: rgba(255, 69, 58, 0.8);
131
98
  }
132
99
 
133
- .api_key_input__submit_button {
100
+ .submitButton {
134
101
  width: 100%;
135
- height: 48px;
102
+ height: 42px;
103
+ background: #5856d6;
136
104
  border: none;
137
105
  border-radius: 6px;
138
- background-color: #5856d6;
139
106
  color: white;
140
107
  font-size: 14px;
141
108
  font-weight: 500;
@@ -143,9 +110,13 @@
143
110
  transition: all 0.2s ease;
144
111
  outline: none;
145
112
 
146
- @media (max-width: 480px) {
147
- height: 44px;
148
- font-size: 16px;
113
+ &:hover:not(:disabled) {
114
+ background: #4845c7;
115
+ transform: translateY(-1px);
116
+ }
117
+
118
+ &:active:not(:disabled) {
119
+ transform: translateY(0);
149
120
  }
150
121
 
151
122
  &:disabled {
@@ -153,4 +124,41 @@
153
124
  cursor: not-allowed;
154
125
  transform: none;
155
126
  }
127
+
128
+ &.loading {
129
+ position: relative;
130
+ color: transparent;
131
+
132
+ &::after {
133
+ content: '';
134
+ position: absolute;
135
+ top: 50%;
136
+ left: 50%;
137
+ width: 16px;
138
+ height: 16px;
139
+ margin-left: -8px;
140
+ margin-top: -8px;
141
+ border: 2px solid transparent;
142
+ border-top: 2px solid white;
143
+ border-radius: 50%;
144
+ animation: spin 1s linear infinite;
145
+ }
146
+ }
147
+ }
148
+
149
+ @keyframes spin {
150
+ 0% {
151
+ transform: rotate(0deg);
152
+ }
153
+ 100% {
154
+ transform: rotate(360deg);
155
+ }
156
+ }
157
+
158
+ // 響應式設計
159
+ @media (max-width: 280px) {
160
+ .container {
161
+ width: 100%;
162
+ min-width: 200px;
163
+ }
156
164
  }
@@ -1,10 +1,6 @@
1
1
  import { useState, FormEvent, ChangeEvent } from 'react';
2
2
  import clsx from 'clsx';
3
3
  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
4
  import styles from './api-key-input.module.scss';
9
5
 
10
6
  export interface ApiKeyInputProps {
@@ -12,7 +8,6 @@ export interface ApiKeyInputProps {
12
8
  loading?: boolean;
13
9
  error?: string;
14
10
  placeholder?: string;
15
- title?: string;
16
11
  showToggle?: boolean;
17
12
  className?: string;
18
13
  }
@@ -22,14 +17,12 @@ export function ApiKeyInput({
22
17
  loading = false,
23
18
  error,
24
19
  placeholder = 'Enter your key',
25
- title = 'Preview',
26
20
  showToggle = true,
27
21
  className,
28
22
  }: ApiKeyInputProps): JSX.Element {
29
23
  const [apiKey, setApiKey] = useState('');
30
24
  const [showPassword, setShowPassword] = useState(false);
31
25
  const { chatbot } = useAsgardThemeContext();
32
- const { avatar } = useAsgardContext();
33
26
 
34
27
  const handleSubmit = (e: FormEvent): void => {
35
28
  e.preventDefault();
@@ -48,29 +41,24 @@ export function ApiKeyInput({
48
41
 
49
42
  return (
50
43
  <div
51
- className={clsx(styles.api_key_input, className)}
44
+ className={clsx(styles.container, className)}
52
45
  style={{
53
46
  backgroundColor: chatbot.backgroundColor,
54
47
  borderColor: chatbot.borderColor,
55
48
  }}
56
49
  >
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>
60
- </div>
61
-
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}>
50
+ <form onSubmit={handleSubmit} className={styles.form}>
51
+ <div className={styles.inputGroup}>
52
+ <label className={styles.label}>Key</label>
53
+ <div className={styles.inputWrapper}>
66
54
  <input
67
55
  type={showPassword ? 'text' : 'password'}
68
56
  value={apiKey}
69
57
  onChange={handleInputChange}
70
58
  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,
59
+ className={clsx(styles.input, {
60
+ [styles.error]: error,
61
+ [styles.disabled]: loading,
74
62
  })}
75
63
  disabled={loading}
76
64
  autoComplete="off"
@@ -79,29 +67,45 @@ export function ApiKeyInput({
79
67
  <button
80
68
  type="button"
81
69
  onClick={togglePasswordVisibility}
82
- className={styles.api_key_input__toggle_button}
70
+ className={styles.toggleButton}
83
71
  disabled={loading}
84
72
  aria-label={showPassword ? 'Hide password' : 'Show password'}
85
73
  >
86
- {showPassword ? (
87
- <EyeOffSvg className={styles.api_key_input__toggle_icon} />
88
- ) : (
89
- <EyeSvg className={styles.api_key_input__toggle_icon} />
90
- )}
74
+ <svg
75
+ className={styles.toggleIcon}
76
+ width="16"
77
+ height="16"
78
+ viewBox="0 0 24 24"
79
+ fill="none"
80
+ stroke="currentColor"
81
+ strokeWidth="2"
82
+ strokeLinecap="round"
83
+ strokeLinejoin="round"
84
+ >
85
+ {showPassword ? (
86
+ <>
87
+ <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" />
88
+ <line x1="1" y1="1" x2="23" y2="23" />
89
+ </>
90
+ ) : (
91
+ <>
92
+ <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z" />
93
+ <circle cx="12" cy="12" r="3" />
94
+ </>
95
+ )}
96
+ </svg>
91
97
  </button>
92
98
  )}
93
99
  </div>
94
- {error && <div className={styles.api_key_input__error_message}>{error}</div>}
100
+ {error && <div className={styles.errorMessage}>{error}</div>}
95
101
  </div>
96
102
 
97
103
  <button
98
104
  type="submit"
99
105
  disabled={!apiKey.trim() || loading}
100
- className={styles.api_key_input__submit_button}
101
- style={{
102
- backgroundColor: chatbot?.mainColor,
103
- color: chatbot?.secondaryColor,
104
- }}
106
+ className={clsx(styles.submitButton, {
107
+ [styles.loading]: loading,
108
+ })}
105
109
  >
106
110
  {loading ? 'Loading...' : 'Continue'}
107
111
  </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>