@asgard-js/react 0.0.40-canary.2 → 0.0.40

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.40-canary.2",
3
+ "version": "0.0.40",
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.38",
57
+ "@asgard-js/core": "^0.0.40",
58
58
  "react": "^18.0.0",
59
59
  "react-dom": "^18.0.0"
60
60
  },
@@ -1,9 +1,4 @@
1
- import {
2
- forwardRef,
3
- ForwardedRef,
4
- ReactNode,
5
- CSSProperties,
6
- } from 'react';
1
+ import { forwardRef, ForwardedRef, ReactNode, CSSProperties } from 'react';
7
2
  import { ClientConfig, ConversationMessage } from '@asgard-js/core';
8
3
  import {
9
4
  AsgardThemeContextProvider,
@@ -17,14 +12,11 @@ import {
17
12
  AsgardAppInitializationContextProvider,
18
13
  AsgardServiceContextProviderProps,
19
14
  } from '../../context';
20
- import { ApiKeyInput } from './api-key-input';
21
15
  import { ChatbotHeader } from './chatbot-header';
22
16
  import { ChatbotBody } from './chatbot-body';
23
17
  import { ChatbotFooter } from './chatbot-footer';
24
18
  import { ChatbotContainer } from './chatbot-container/chatbot-container';
25
19
 
26
- type AuthState = 'loading' | 'needApiKey' | 'authenticated' | 'error';
27
-
28
20
  interface ChatbotProps extends AsgardTemplateContextValue {
29
21
  className?: string;
30
22
  style?: CSSProperties;
@@ -46,10 +38,6 @@ interface ChatbotProps extends AsgardTemplateContextValue {
46
38
  onClose?: () => void;
47
39
  loadingComponent?: ReactNode;
48
40
  defaultLinkTarget?: '_blank' | '_self' | '_parent' | '_top';
49
-
50
- // Auth state props
51
- authState?: AuthState;
52
- onApiKeySubmit?: (apiKey: string) => Promise<void>;
53
41
  }
54
42
 
55
43
  export interface ChatbotRef {
@@ -84,78 +72,8 @@ export const Chatbot = forwardRef(function Chatbot(
84
72
  className,
85
73
  style,
86
74
  defaultLinkTarget,
87
- authState = 'authenticated',
88
- onApiKeySubmit,
89
75
  } = props;
90
76
 
91
- // Render different content based on authState
92
- const renderContent = () => {
93
- switch (authState) {
94
- case 'loading':
95
- return (
96
- <div style={{
97
- display: 'flex',
98
- alignItems: 'center',
99
- justifyContent: 'center',
100
- flex: 1,
101
- padding: '20px'
102
- }}>
103
- {loadingComponent || <div>Loading...</div>}
104
- </div>
105
- );
106
-
107
- case 'needApiKey':
108
- return (
109
- <div style={{
110
- display: 'flex',
111
- alignItems: 'center',
112
- justifyContent: 'center',
113
- flex: 1,
114
- padding: '20px'
115
- }}>
116
- <ApiKeyInput
117
- title={title}
118
- onSubmit={onApiKeySubmit || (() => {})}
119
- placeholder="Enter your key"
120
- />
121
- </div>
122
- );
123
-
124
- case 'error':
125
- return (
126
- <div style={{
127
- display: 'flex',
128
- alignItems: 'center',
129
- justifyContent: 'center',
130
- flex: 1,
131
- padding: '20px',
132
- color: '#ff6b6b'
133
- }}>
134
- <div style={{ textAlign: 'center' }}>
135
- <div style={{ fontSize: '16px', marginBottom: '8px' }}>⚠️</div>
136
- <div>Something went wrong. Please try again later.</div>
137
- </div>
138
- </div>
139
- );
140
-
141
- case 'authenticated':
142
- default:
143
- return (
144
- <>
145
- <AsgardTemplateContextProvider
146
- onErrorClick={onErrorClick}
147
- errorMessageRenderer={errorMessageRenderer}
148
- onTemplateBtnClick={onTemplateBtnClick}
149
- defaultLinkTarget={defaultLinkTarget}
150
- >
151
- <ChatbotBody />
152
- </AsgardTemplateContextProvider>
153
- <ChatbotFooter />
154
- </>
155
- );
156
- }
157
- };
158
-
159
77
  return (
160
78
  <AsgardAppInitializationContextProvider
161
79
  enabled={enableLoadConfigFromService}
@@ -186,7 +104,15 @@ export const Chatbot = forwardRef(function Chatbot(
186
104
  customActions={customActions}
187
105
  maintainConnectionWhenClosed={maintainConnectionWhenClosed}
188
106
  />
189
- {renderContent()}
107
+ <AsgardTemplateContextProvider
108
+ onErrorClick={onErrorClick}
109
+ errorMessageRenderer={errorMessageRenderer}
110
+ onTemplateBtnClick={onTemplateBtnClick}
111
+ defaultLinkTarget={defaultLinkTarget}
112
+ >
113
+ <ChatbotBody />
114
+ </AsgardTemplateContextProvider>
115
+ <ChatbotFooter />
190
116
  </ChatbotContainer>
191
117
  </AsgardServiceContextProvider>
192
118
  </AsgardThemeContextProvider>
@@ -366,7 +366,7 @@ export function AsgardThemeContextProvider(
366
366
  ButtonMessageTemplate: {
367
367
  button: {
368
368
  style: {
369
- borderColor: themeFromAnnotations.chatbot?.borderColor,
369
+ borderColor: themeFromAnnotations.chatbot?.primaryComponent?.mainColor,
370
370
  backgroundColor:
371
371
  themeFromAnnotations.chatbot?.primaryComponent?.mainColor,
372
372
  color:
@@ -384,7 +384,7 @@ export function AsgardThemeContextProvider(
384
384
  },
385
385
  button: {
386
386
  style: {
387
- borderColor: themeFromAnnotations.chatbot?.borderColor,
387
+ borderColor: themeFromAnnotations.chatbot?.primaryComponent?.mainColor,
388
388
  backgroundColor:
389
389
  themeFromAnnotations.chatbot?.primaryComponent?.mainColor,
390
390
  color:
@@ -501,7 +501,7 @@ export function AsgardThemeContextProvider(
501
501
  ButtonMessageTemplate: {
502
502
  button: {
503
503
  style: {
504
- borderColor: propsTheme.chatbot?.borderColor,
504
+ borderColor: propsTheme.chatbot?.primaryComponent?.mainColor,
505
505
  backgroundColor: propsTheme.chatbot?.primaryComponent?.mainColor,
506
506
  color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
507
507
  },
@@ -514,7 +514,7 @@ export function AsgardThemeContextProvider(
514
514
  },
515
515
  button: {
516
516
  style: {
517
- borderColor: propsTheme.chatbot?.borderColor,
517
+ borderColor: propsTheme.chatbot?.primaryComponent?.mainColor,
518
518
  backgroundColor: propsTheme.chatbot?.primaryComponent?.mainColor,
519
519
  color: propsTheme.chatbot?.primaryComponent?.secondaryColor,
520
520
  },
@@ -1,11 +0,0 @@
1
- export interface ApiKeyInputProps {
2
- onSubmit: (apiKey: string) => void;
3
- loading?: boolean;
4
- error?: string;
5
- placeholder?: string;
6
- title?: string;
7
- showToggle?: boolean;
8
- className?: string;
9
- }
10
- export declare function ApiKeyInput({ onSubmit, loading, error, placeholder, title, showToggle, className, }: ApiKeyInputProps): import("react/jsx-runtime").JSX.Element;
11
- //# sourceMappingURL=api-key-input.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"api-key-input.d.ts","sourceRoot":"","sources":["../../../../src/components/chatbot/api-key-input/api-key-input.tsx"],"names":[],"mappings":"AAKA,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,WAAW,CAAC,EAC1B,QAAQ,EACR,OAAe,EACf,KAAK,EACL,WAA8B,EAC9B,KAAiB,EACjB,UAAiB,EACjB,SAAS,GACV,EAAE,gBAAgB,2CA+FlB"}
@@ -1,2 +0,0 @@
1
- export * from './api-key-input';
2
- //# sourceMappingURL=index.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/chatbot/api-key-input/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC"}
@@ -1,192 +0,0 @@
1
- .container {
2
- width: 220px;
3
- background: rgba(45, 45, 45, 0.95);
4
- border-radius: 12px;
5
- padding: 24px 20px 20px;
6
- backdrop-filter: blur(10px);
7
- border: 1px solid rgba(255, 255, 255, 0.1);
8
- box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
9
- color: white;
10
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif;
11
- }
12
-
13
- .header {
14
- display: flex;
15
- flex-direction: column;
16
- align-items: center;
17
- margin-bottom: 24px;
18
- }
19
-
20
- .icon {
21
- width: 48px;
22
- height: 48px;
23
- margin-bottom: 12px;
24
- opacity: 0.7;
25
- }
26
-
27
- .title {
28
- margin: 0;
29
- font-size: 18px;
30
- font-weight: 500;
31
- color: white;
32
- text-align: center;
33
- }
34
-
35
- .form {
36
- width: 100%;
37
- }
38
-
39
- .inputGroup {
40
- margin-bottom: 20px;
41
- }
42
-
43
- .label {
44
- display: block;
45
- font-size: 14px;
46
- font-weight: 400;
47
- color: rgba(255, 255, 255, 0.7);
48
- margin-bottom: 8px;
49
- }
50
-
51
- .inputWrapper {
52
- position: relative;
53
- width: 100%;
54
- }
55
-
56
- .input {
57
- width: 100%;
58
- height: 42px;
59
- padding: 0 40px 0 12px;
60
- font-size: 14px;
61
- font-weight: 400;
62
- color: rgba(255, 255, 255, 0.8);
63
- background: rgba(255, 255, 255, 0.05);
64
- border: 1px solid rgba(255, 255, 255, 0.1);
65
- border-radius: 6px;
66
- outline: none;
67
- transition: all 0.2s ease;
68
- box-sizing: border-box;
69
-
70
- &::placeholder {
71
- color: rgba(255, 255, 255, 0.4);
72
- }
73
-
74
- &:focus {
75
- border-color: rgba(255, 255, 255, 0.3);
76
- background: rgba(255, 255, 255, 0.08);
77
- }
78
-
79
- &.error {
80
- border-color: rgba(255, 69, 58, 0.6);
81
- background: rgba(255, 69, 58, 0.05);
82
- }
83
-
84
- &.disabled {
85
- opacity: 0.5;
86
- cursor: not-allowed;
87
- }
88
- }
89
-
90
- .toggleButton {
91
- position: absolute;
92
- right: 8px;
93
- top: 50%;
94
- transform: translateY(-50%);
95
- background: transparent;
96
- border: none;
97
- color: rgba(255, 255, 255, 0.5);
98
- cursor: pointer;
99
- padding: 4px;
100
- border-radius: 4px;
101
- transition: all 0.2s ease;
102
- display: flex;
103
- align-items: center;
104
- justify-content: center;
105
-
106
- &:hover:not(:disabled) {
107
- color: rgba(255, 255, 255, 0.7);
108
- background: rgba(255, 255, 255, 0.05);
109
- }
110
-
111
- &:disabled {
112
- opacity: 0.3;
113
- cursor: not-allowed;
114
- }
115
- }
116
-
117
- .toggleIcon {
118
- width: 16px;
119
- height: 16px;
120
- }
121
-
122
- .errorMessage {
123
- margin-top: 6px;
124
- font-size: 12px;
125
- color: rgba(255, 69, 58, 0.8);
126
- }
127
-
128
- .submitButton {
129
- width: 100%;
130
- height: 42px;
131
- background: #5856D6;
132
- border: none;
133
- border-radius: 6px;
134
- color: white;
135
- font-size: 14px;
136
- font-weight: 500;
137
- cursor: pointer;
138
- transition: all 0.2s ease;
139
- outline: none;
140
-
141
- &:hover:not(:disabled) {
142
- background: #4845C7;
143
- transform: translateY(-1px);
144
- }
145
-
146
- &:active:not(:disabled) {
147
- transform: translateY(0);
148
- }
149
-
150
- &:disabled {
151
- opacity: 0.5;
152
- cursor: not-allowed;
153
- transform: none;
154
- }
155
-
156
- &.loading {
157
- position: relative;
158
- color: transparent;
159
-
160
- &::after {
161
- content: '';
162
- position: absolute;
163
- top: 50%;
164
- left: 50%;
165
- width: 16px;
166
- height: 16px;
167
- margin-left: -8px;
168
- margin-top: -8px;
169
- border: 2px solid transparent;
170
- border-top: 2px solid white;
171
- border-radius: 50%;
172
- animation: spin 1s linear infinite;
173
- }
174
- }
175
- }
176
-
177
- @keyframes spin {
178
- 0% {
179
- transform: rotate(0deg);
180
- }
181
- 100% {
182
- transform: rotate(360deg);
183
- }
184
- }
185
-
186
- // 響應式設計
187
- @media (max-width: 280px) {
188
- .container {
189
- width: 100%;
190
- min-width: 200px;
191
- }
192
- }
@@ -1,119 +0,0 @@
1
- import { useState, FormEvent, ChangeEvent } from 'react';
2
- import clsx from 'clsx';
3
- import ProfileSvg from '../../../icons/profile.svg?react';
4
- import styles from './api-key-input.module.scss';
5
-
6
- export interface ApiKeyInputProps {
7
- onSubmit: (apiKey: string) => void;
8
- loading?: boolean;
9
- error?: string;
10
- placeholder?: string;
11
- title?: string;
12
- showToggle?: boolean;
13
- className?: string;
14
- }
15
-
16
- export function ApiKeyInput({
17
- onSubmit,
18
- loading = false,
19
- error,
20
- placeholder = 'Enter your key',
21
- title = 'Preview',
22
- showToggle = true,
23
- className,
24
- }: ApiKeyInputProps) {
25
- const [apiKey, setApiKey] = useState('');
26
- const [showPassword, setShowPassword] = useState(false);
27
-
28
- const handleSubmit = (e: FormEvent) => {
29
- e.preventDefault();
30
- if (apiKey.trim() && !loading) {
31
- onSubmit(apiKey.trim());
32
- }
33
- };
34
-
35
- const handleInputChange = (e: ChangeEvent<HTMLInputElement>) => {
36
- setApiKey(e.target.value);
37
- };
38
-
39
- const togglePasswordVisibility = () => {
40
- setShowPassword(!showPassword);
41
- };
42
-
43
- return (
44
- <div className={clsx(styles.container, className)}>
45
- <div className={styles.header}>
46
- <ProfileSvg className={styles.icon} />
47
- <h2 className={styles.title}>{title}</h2>
48
- </div>
49
-
50
- <form onSubmit={handleSubmit} className={styles.form}>
51
- <div className={styles.inputGroup}>
52
- <label className={styles.label}>Key</label>
53
- <div className={styles.inputWrapper}>
54
- <input
55
- type={showPassword ? 'text' : 'password'}
56
- value={apiKey}
57
- onChange={handleInputChange}
58
- placeholder={placeholder}
59
- className={clsx(styles.input, {
60
- [styles.error]: error,
61
- [styles.disabled]: loading,
62
- })}
63
- disabled={loading}
64
- autoComplete="off"
65
- />
66
- {showToggle && (
67
- <button
68
- type="button"
69
- onClick={togglePasswordVisibility}
70
- className={styles.toggleButton}
71
- disabled={loading}
72
- aria-label={showPassword ? 'Hide password' : 'Show password'}
73
- >
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>
97
- </button>
98
- )}
99
- </div>
100
- {error && (
101
- <div className={styles.errorMessage}>
102
- {error}
103
- </div>
104
- )}
105
- </div>
106
-
107
- <button
108
- type="submit"
109
- disabled={!apiKey.trim() || loading}
110
- className={clsx(styles.submitButton, {
111
- [styles.loading]: loading,
112
- })}
113
- >
114
- {loading ? 'Loading...' : 'Continue'}
115
- </button>
116
- </form>
117
- </div>
118
- );
119
- }
@@ -1 +0,0 @@
1
- export * from './api-key-input';