@chainai/react 1.0.0
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/README.md +227 -0
- package/dist/components/ActionPrompt.d.ts +22 -0
- package/dist/components/ActionPrompt.js +130 -0
- package/dist/components/ChainAI.d.ts +19 -0
- package/dist/components/ChainAI.js +89 -0
- package/dist/components/ChainAIWidget.d.ts +86 -0
- package/dist/components/ChainAIWidget.js +243 -0
- package/dist/components/ChatHeader.d.ts +11 -0
- package/dist/components/ChatHeader.js +20 -0
- package/dist/components/ChatInput.d.ts +12 -0
- package/dist/components/ChatInput.js +65 -0
- package/dist/components/ChatMessage.d.ts +11 -0
- package/dist/components/ChatMessage.js +20 -0
- package/dist/context/ChainAIProvider.d.ts +19 -0
- package/dist/context/ChainAIProvider.js +95 -0
- package/dist/hooks/useChat.d.ts +12 -0
- package/dist/hooks/useChat.js +80 -0
- package/dist/index.d.ts +24 -0
- package/dist/index.js +34 -0
- package/dist/styles/themes.d.ts +31 -0
- package/dist/styles/themes.js +49 -0
- package/package.json +67 -0
- package/styles/default.css +156 -0
- package/styles/widget.css +531 -0
package/README.md
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
# @chain-ai/react
|
|
2
|
+
|
|
3
|
+
React components for Chain AI - Embeddable blockchain AI assistant.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @chain-ai/react @chain-ai/core
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { ChainAI } from '@chain-ai/react';
|
|
15
|
+
|
|
16
|
+
function App() {
|
|
17
|
+
return (
|
|
18
|
+
<ChainAI
|
|
19
|
+
config={{ apiKey: 'sk_dev_123_...' }}
|
|
20
|
+
height="600px"
|
|
21
|
+
placeholder="Ask me anything about blockchain..."
|
|
22
|
+
/>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Features
|
|
28
|
+
|
|
29
|
+
- ✅ **Simplified Components** - 94% reduction from 2,556 lines to ~360 lines
|
|
30
|
+
- ✅ **Customizable Theming** - Dark/light themes with full CSS customization
|
|
31
|
+
- ✅ **Real-time Streaming** - Word-by-word response streaming
|
|
32
|
+
- ✅ **Type Safe** - Full TypeScript support
|
|
33
|
+
- ✅ **Responsive** - Works on all screen sizes
|
|
34
|
+
- ✅ **Accessible** - Keyboard navigation support
|
|
35
|
+
|
|
36
|
+
## Basic Usage
|
|
37
|
+
|
|
38
|
+
### Standalone Component
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { ChainAI } from '@chain-ai/react';
|
|
42
|
+
|
|
43
|
+
function App() {
|
|
44
|
+
return (
|
|
45
|
+
<ChainAI
|
|
46
|
+
config={{
|
|
47
|
+
apiKey: 'sk_dev_123_...',
|
|
48
|
+
host: 'https://icp0.io', // optional
|
|
49
|
+
}}
|
|
50
|
+
height="600px"
|
|
51
|
+
theme={darkTheme}
|
|
52
|
+
placeholder="Message Chain AI..."
|
|
53
|
+
showHeader={true}
|
|
54
|
+
headerTitle="Blockchain Assistant"
|
|
55
|
+
onMessage={(msg) => console.log('User sent:', msg)}
|
|
56
|
+
/>
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Advanced Usage with Provider
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
import { ChainAIProvider, useChainAI, useChat } from '@chain-ai/react';
|
|
65
|
+
|
|
66
|
+
function App() {
|
|
67
|
+
return (
|
|
68
|
+
<ChainAIProvider
|
|
69
|
+
config={{
|
|
70
|
+
apiKey: 'sk_dev_123_...',
|
|
71
|
+
}}
|
|
72
|
+
>
|
|
73
|
+
<MyCustomChat />
|
|
74
|
+
</ChainAIProvider>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function MyCustomChat() {
|
|
79
|
+
const { messages, sendMessage, isLoading } = useChat();
|
|
80
|
+
|
|
81
|
+
return (
|
|
82
|
+
<div>
|
|
83
|
+
{messages.map(msg => (
|
|
84
|
+
<div key={msg.id}>{msg.content}</div>
|
|
85
|
+
))}
|
|
86
|
+
<button onClick={() => sendMessage('What is BTC price?')}>
|
|
87
|
+
Get BTC Price
|
|
88
|
+
</button>
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Props
|
|
95
|
+
|
|
96
|
+
### ChainAI Component
|
|
97
|
+
|
|
98
|
+
| Prop | Type | Default | Description |
|
|
99
|
+
|------|------|---------|-------------|
|
|
100
|
+
| `config` | `ChainAIConfig` | **required** | API key and configuration |
|
|
101
|
+
| `conversationId` | `string` | `'default'` | Unique conversation identifier |
|
|
102
|
+
| `theme` | `Theme` | `defaultTheme` | Color scheme and styling |
|
|
103
|
+
| `className` | `string` | `''` | Additional CSS classes |
|
|
104
|
+
| `height` | `string \| number` | `'600px'` | Component height |
|
|
105
|
+
| `placeholder` | `string` | `'Ask me anything...'` | Input placeholder text |
|
|
106
|
+
| `showHeader` | `boolean` | `true` | Show/hide header bar |
|
|
107
|
+
| `headerTitle` | `string` | `'Chain AI'` | Header title text |
|
|
108
|
+
| `onMessage` | `(msg: string) => void` | - | Callback when user sends message |
|
|
109
|
+
|
|
110
|
+
## Theming
|
|
111
|
+
|
|
112
|
+
### Built-in Themes
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
import { ChainAI, darkTheme, lightTheme } from '@chain-ai/react';
|
|
116
|
+
|
|
117
|
+
// Dark theme
|
|
118
|
+
<ChainAI config={config} theme={darkTheme} />
|
|
119
|
+
|
|
120
|
+
// Light theme
|
|
121
|
+
<ChainAI config={config} theme={lightTheme} />
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Custom Theme
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { ChainAI, Theme } from '@chain-ai/react';
|
|
128
|
+
|
|
129
|
+
const customTheme: Theme = {
|
|
130
|
+
colors: {
|
|
131
|
+
primary: '#6366f1',
|
|
132
|
+
primaryText: '#ffffff',
|
|
133
|
+
background: '#0f172a',
|
|
134
|
+
text: '#f8fafc',
|
|
135
|
+
border: '#1e293b',
|
|
136
|
+
userMessage: '#6366f1',
|
|
137
|
+
userMessageText: '#ffffff',
|
|
138
|
+
assistantMessage: '#1e293b',
|
|
139
|
+
assistantMessageText: '#f8fafc',
|
|
140
|
+
inputBackground: '#1e293b',
|
|
141
|
+
headerBackground: '#020617',
|
|
142
|
+
headerText: '#f8fafc',
|
|
143
|
+
},
|
|
144
|
+
fonts: {
|
|
145
|
+
body: 'Inter, sans-serif',
|
|
146
|
+
heading: 'Inter, sans-serif',
|
|
147
|
+
},
|
|
148
|
+
spacing: {
|
|
149
|
+
small: '8px',
|
|
150
|
+
medium: '16px',
|
|
151
|
+
large: '24px',
|
|
152
|
+
},
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
<ChainAI config={config} theme={customTheme} />
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Hooks
|
|
159
|
+
|
|
160
|
+
### useChainAI()
|
|
161
|
+
|
|
162
|
+
Access the Chain AI client and customization settings.
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
const { client, isReady, error, customization, updateCustomization } = useChainAI();
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### useChat(conversationId?)
|
|
169
|
+
|
|
170
|
+
Manage chat messages and state.
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
const { messages, isLoading, error, sendMessage, clearMessages } = useChat('my-conversation');
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Components
|
|
177
|
+
|
|
178
|
+
### Individual Components
|
|
179
|
+
|
|
180
|
+
You can use individual components for custom layouts:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import {
|
|
184
|
+
ChainAIProvider,
|
|
185
|
+
ChatMessage,
|
|
186
|
+
ChatInput,
|
|
187
|
+
ChatHeader,
|
|
188
|
+
useChat,
|
|
189
|
+
defaultTheme,
|
|
190
|
+
} from '@chain-ai/react';
|
|
191
|
+
|
|
192
|
+
function CustomLayout() {
|
|
193
|
+
const { messages, sendMessage, isLoading } = useChat();
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<div>
|
|
197
|
+
<ChatHeader title="My Custom Chat" theme={defaultTheme} />
|
|
198
|
+
{messages.map(msg => (
|
|
199
|
+
<ChatMessage key={msg.id} message={msg} theme={defaultTheme} />
|
|
200
|
+
))}
|
|
201
|
+
<ChatInput
|
|
202
|
+
onSend={sendMessage}
|
|
203
|
+
disabled={isLoading}
|
|
204
|
+
theme={defaultTheme}
|
|
205
|
+
/>
|
|
206
|
+
</div>
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## TypeScript
|
|
212
|
+
|
|
213
|
+
Full TypeScript support with exported types:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import type {
|
|
217
|
+
ChainAIProps,
|
|
218
|
+
Theme,
|
|
219
|
+
Message,
|
|
220
|
+
ChainAIConfig,
|
|
221
|
+
ResponseCustomization,
|
|
222
|
+
} from '@chain-ai/react';
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ActionPrompt - UI for handling incomplete actions
|
|
3
|
+
*
|
|
4
|
+
* Displays missing fields and allows users to fill them in
|
|
5
|
+
*/
|
|
6
|
+
import React from 'react';
|
|
7
|
+
import type { Theme } from '../styles/themes';
|
|
8
|
+
export interface ActionPromptProps {
|
|
9
|
+
/** The action type (e.g., 'send', 'swap') */
|
|
10
|
+
action: string;
|
|
11
|
+
/** List of missing required fields */
|
|
12
|
+
missing: string[];
|
|
13
|
+
/** AI-generated prompt asking for the missing fields */
|
|
14
|
+
prompt: string;
|
|
15
|
+
/** Called when user submits a field value */
|
|
16
|
+
onSubmit: (field: string, value: string) => void;
|
|
17
|
+
/** Theme configuration */
|
|
18
|
+
theme: Theme;
|
|
19
|
+
/** Additional CSS class */
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
export declare const ActionPrompt: React.FC<ActionPromptProps>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ActionPrompt - UI for handling incomplete actions
|
|
4
|
+
*
|
|
5
|
+
* Displays missing fields and allows users to fill them in
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.ActionPrompt = void 0;
|
|
42
|
+
const react_1 = __importStar(require("react"));
|
|
43
|
+
// Field-specific input configurations
|
|
44
|
+
const fieldConfig = {
|
|
45
|
+
amount: { type: 'number', placeholder: '0.00', label: 'Amount' },
|
|
46
|
+
recipient: { type: 'text', placeholder: '0x... or wallet address', label: 'Recipient Address' },
|
|
47
|
+
token: { type: 'text', placeholder: 'ETH, BTC, USDC...', label: 'Token' },
|
|
48
|
+
address: { type: 'text', placeholder: '0x... or wallet address', label: 'Address' },
|
|
49
|
+
fee: { type: 'text', placeholder: 'low, medium, or high', label: 'Fee Priority' },
|
|
50
|
+
memo: { type: 'text', placeholder: 'Optional note', label: 'Memo' },
|
|
51
|
+
chain: { type: 'text', placeholder: 'Ethereum, Bitcoin...', label: 'Chain' },
|
|
52
|
+
slippage: { type: 'number', placeholder: '0.5', label: 'Slippage %' },
|
|
53
|
+
};
|
|
54
|
+
// Action icons
|
|
55
|
+
const ActionIcon = ({ action }) => {
|
|
56
|
+
switch (action) {
|
|
57
|
+
case 'send':
|
|
58
|
+
return (react_1.default.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
|
|
59
|
+
react_1.default.createElement("line", { x1: "22", y1: "2", x2: "11", y2: "13" }),
|
|
60
|
+
react_1.default.createElement("polygon", { points: "22 2 15 22 11 13 2 9 22 2" })));
|
|
61
|
+
case 'swap':
|
|
62
|
+
return (react_1.default.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
|
|
63
|
+
react_1.default.createElement("polyline", { points: "17 1 21 5 17 9" }),
|
|
64
|
+
react_1.default.createElement("path", { d: "M3 11V9a4 4 0 0 1 4-4h14" }),
|
|
65
|
+
react_1.default.createElement("polyline", { points: "7 23 3 19 7 15" }),
|
|
66
|
+
react_1.default.createElement("path", { d: "M21 13v2a4 4 0 0 1-4 4H3" })));
|
|
67
|
+
default:
|
|
68
|
+
return (react_1.default.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2" },
|
|
69
|
+
react_1.default.createElement("circle", { cx: "12", cy: "12", r: "10" }),
|
|
70
|
+
react_1.default.createElement("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
71
|
+
react_1.default.createElement("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })));
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
const ActionPrompt = ({ action, missing, prompt, onSubmit, theme, className = '', }) => {
|
|
75
|
+
const [values, setValues] = (0, react_1.useState)({});
|
|
76
|
+
const [focusedField, setFocusedField] = (0, react_1.useState)(null);
|
|
77
|
+
const handleChange = (field, value) => {
|
|
78
|
+
setValues((prev) => ({ ...prev, [field]: value }));
|
|
79
|
+
};
|
|
80
|
+
const handleSubmit = (e) => {
|
|
81
|
+
e.preventDefault();
|
|
82
|
+
// Submit all filled values
|
|
83
|
+
const filledFields = Object.entries(values).filter(([_, v]) => v.trim());
|
|
84
|
+
if (filledFields.length > 0) {
|
|
85
|
+
// Send as a combined message
|
|
86
|
+
const message = filledFields.map(([k, v]) => `${k}: ${v}`).join(', ');
|
|
87
|
+
onSubmit(filledFields[0][0], message);
|
|
88
|
+
}
|
|
89
|
+
};
|
|
90
|
+
const handleQuickSelect = (field, value) => {
|
|
91
|
+
setValues((prev) => ({ ...prev, [field]: value }));
|
|
92
|
+
onSubmit(field, `${field}: ${value}`);
|
|
93
|
+
};
|
|
94
|
+
// Quick selection options for certain fields
|
|
95
|
+
const quickOptions = {
|
|
96
|
+
fee: ['low', 'medium', 'high'],
|
|
97
|
+
token: ['ETH', 'BTC', 'USDC', 'USDT'],
|
|
98
|
+
chain: ['Ethereum', 'Bitcoin', 'Solana', 'ICP'],
|
|
99
|
+
};
|
|
100
|
+
return (react_1.default.createElement("div", { className: `chain-ai-action-prompt ${className}` },
|
|
101
|
+
react_1.default.createElement("div", { className: "chain-ai-action-prompt-header" },
|
|
102
|
+
react_1.default.createElement("div", { className: "chain-ai-action-prompt-icon", style: { color: theme.colors.primary } },
|
|
103
|
+
react_1.default.createElement(ActionIcon, { action: action })),
|
|
104
|
+
react_1.default.createElement("div", { className: "chain-ai-action-prompt-title" },
|
|
105
|
+
react_1.default.createElement("span", { className: "chain-ai-action-prompt-action" }, action.toUpperCase()),
|
|
106
|
+
react_1.default.createElement("span", { className: "chain-ai-action-prompt-label" }, "Missing information"))),
|
|
107
|
+
react_1.default.createElement("p", { className: "chain-ai-action-prompt-message" }, prompt),
|
|
108
|
+
react_1.default.createElement("form", { onSubmit: handleSubmit, className: "chain-ai-action-prompt-form" },
|
|
109
|
+
missing.map((field) => {
|
|
110
|
+
const config = fieldConfig[field] || {
|
|
111
|
+
type: 'text',
|
|
112
|
+
placeholder: `Enter ${field}`,
|
|
113
|
+
label: field.charAt(0).toUpperCase() + field.slice(1),
|
|
114
|
+
};
|
|
115
|
+
return (react_1.default.createElement("div", { key: field, className: `chain-ai-action-prompt-field ${focusedField === field ? 'focused' : ''}` },
|
|
116
|
+
react_1.default.createElement("label", { className: "chain-ai-action-prompt-field-label" }, config.label),
|
|
117
|
+
quickOptions[field] && (react_1.default.createElement("div", { className: "chain-ai-action-prompt-quick-options" }, quickOptions[field].map((option) => (react_1.default.createElement("button", { key: option, type: "button", className: "chain-ai-action-prompt-quick-btn", onClick: () => handleQuickSelect(field, option), style: {
|
|
118
|
+
borderColor: theme.colors.border,
|
|
119
|
+
color: theme.colors.text,
|
|
120
|
+
} }, option))))),
|
|
121
|
+
react_1.default.createElement("input", { type: config.type, className: "chain-ai-action-prompt-input", placeholder: config.placeholder, value: values[field] || '', onChange: (e) => handleChange(field, e.target.value), onFocus: () => setFocusedField(field), onBlur: () => setFocusedField(null), style: {
|
|
122
|
+
borderColor: focusedField === field ? theme.colors.primary : theme.colors.border,
|
|
123
|
+
} })));
|
|
124
|
+
}),
|
|
125
|
+
react_1.default.createElement("button", { type: "submit", className: "chain-ai-action-prompt-submit", disabled: Object.values(values).every((v) => !v.trim()), style: {
|
|
126
|
+
backgroundColor: theme.colors.primary,
|
|
127
|
+
color: theme.colors.primaryText,
|
|
128
|
+
} }, "Continue"))));
|
|
129
|
+
};
|
|
130
|
+
exports.ActionPrompt = ActionPrompt;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChainAI - Main chat component (simplified from 2,556-line playground)
|
|
3
|
+
*/
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import type { ChainAIConfig } from '@chainai/core';
|
|
6
|
+
import type { Theme } from '../styles/themes';
|
|
7
|
+
import '../styles/default.css';
|
|
8
|
+
export interface ChainAIProps {
|
|
9
|
+
config: ChainAIConfig;
|
|
10
|
+
conversationId?: string;
|
|
11
|
+
theme?: Theme;
|
|
12
|
+
className?: string;
|
|
13
|
+
height?: string | number;
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
showHeader?: boolean;
|
|
16
|
+
headerTitle?: string;
|
|
17
|
+
onMessage?: (message: string) => void;
|
|
18
|
+
}
|
|
19
|
+
export declare const ChainAI: React.FC<ChainAIProps>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* ChainAI - Main chat component (simplified from 2,556-line playground)
|
|
4
|
+
*/
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
17
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
18
|
+
}) : function(o, v) {
|
|
19
|
+
o["default"] = v;
|
|
20
|
+
});
|
|
21
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
22
|
+
var ownKeys = function(o) {
|
|
23
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
24
|
+
var ar = [];
|
|
25
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
26
|
+
return ar;
|
|
27
|
+
};
|
|
28
|
+
return ownKeys(o);
|
|
29
|
+
};
|
|
30
|
+
return function (mod) {
|
|
31
|
+
if (mod && mod.__esModule) return mod;
|
|
32
|
+
var result = {};
|
|
33
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
34
|
+
__setModuleDefault(result, mod);
|
|
35
|
+
return result;
|
|
36
|
+
};
|
|
37
|
+
})();
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.ChainAI = void 0;
|
|
40
|
+
const react_1 = __importStar(require("react"));
|
|
41
|
+
const ChainAIProvider_1 = require("../context/ChainAIProvider");
|
|
42
|
+
const useChat_1 = require("../hooks/useChat");
|
|
43
|
+
const ChatMessage_1 = require("./ChatMessage");
|
|
44
|
+
const ChatInput_1 = require("./ChatInput");
|
|
45
|
+
const ChatHeader_1 = require("./ChatHeader");
|
|
46
|
+
const themes_1 = require("../styles/themes");
|
|
47
|
+
require("../styles/default.css");
|
|
48
|
+
const ChainAIInner = ({ conversationId, theme = themes_1.defaultTheme, className = '', height = '600px', placeholder = 'Ask me anything about blockchain...', showHeader = true, headerTitle = 'Chain AI', onMessage, }) => {
|
|
49
|
+
const { isReady, error: clientError } = (0, ChainAIProvider_1.useChainAI)();
|
|
50
|
+
const { messages, isLoading, error, sendMessage } = (0, useChat_1.useChat)(conversationId);
|
|
51
|
+
const messagesEndRef = (0, react_1.useRef)(null);
|
|
52
|
+
// Auto-scroll to bottom when new messages arrive
|
|
53
|
+
(0, react_1.useEffect)(() => {
|
|
54
|
+
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
|
|
55
|
+
}, [messages]);
|
|
56
|
+
const handleSend = async (text) => {
|
|
57
|
+
onMessage?.(text);
|
|
58
|
+
await sendMessage(text);
|
|
59
|
+
};
|
|
60
|
+
if (!isReady) {
|
|
61
|
+
return (react_1.default.createElement("div", { className: `chain-ai-container ${className}`, style: { height } },
|
|
62
|
+
react_1.default.createElement("div", { className: "chain-ai-loading" }, "Initializing Chain AI...")));
|
|
63
|
+
}
|
|
64
|
+
if (clientError) {
|
|
65
|
+
return (react_1.default.createElement("div", { className: `chain-ai-container ${className}`, style: { height } },
|
|
66
|
+
react_1.default.createElement("div", { className: "chain-ai-error" },
|
|
67
|
+
"Error: ",
|
|
68
|
+
clientError)));
|
|
69
|
+
}
|
|
70
|
+
return (react_1.default.createElement("div", { className: `chain-ai-container ${className}`, style: {
|
|
71
|
+
height,
|
|
72
|
+
backgroundColor: theme.colors.background,
|
|
73
|
+
color: theme.colors.text,
|
|
74
|
+
} },
|
|
75
|
+
showHeader && react_1.default.createElement(ChatHeader_1.ChatHeader, { title: headerTitle, theme: theme }),
|
|
76
|
+
react_1.default.createElement("div", { className: "chain-ai-messages" },
|
|
77
|
+
messages.length === 0 && (react_1.default.createElement("div", { className: "chain-ai-welcome" },
|
|
78
|
+
react_1.default.createElement("h2", null, "Welcome to Chain AI"),
|
|
79
|
+
react_1.default.createElement("p", null, "Ask me anything about blockchain operations"))),
|
|
80
|
+
messages.map((message) => (react_1.default.createElement(ChatMessage_1.ChatMessage, { key: message.id, message: message, theme: theme }))),
|
|
81
|
+
error && react_1.default.createElement("div", { className: "chain-ai-error" }, error),
|
|
82
|
+
react_1.default.createElement("div", { ref: messagesEndRef })),
|
|
83
|
+
react_1.default.createElement(ChatInput_1.ChatInput, { onSend: handleSend, placeholder: placeholder, disabled: isLoading, theme: theme })));
|
|
84
|
+
};
|
|
85
|
+
const ChainAI = ({ config, ...props }) => {
|
|
86
|
+
return (react_1.default.createElement(ChainAIProvider_1.ChainAIProvider, { config: config },
|
|
87
|
+
react_1.default.createElement(ChainAIInner, { ...props })));
|
|
88
|
+
};
|
|
89
|
+
exports.ChainAI = ChainAI;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ChainAIWidget - Fully customizable floating chat widget
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Floating action button (FAB) that expands to chat
|
|
6
|
+
* - Full branding customization (logo, colors, text)
|
|
7
|
+
* - CSS variables for easy styling
|
|
8
|
+
* - Works in any context (web, extension, mobile)
|
|
9
|
+
*/
|
|
10
|
+
import React from 'react';
|
|
11
|
+
import type { ChainAIConfig } from '@chainai/core';
|
|
12
|
+
import type { Theme } from '../styles/themes';
|
|
13
|
+
import '../styles/widget.css';
|
|
14
|
+
/** Branding configuration for white-labeling */
|
|
15
|
+
export interface BrandingConfig {
|
|
16
|
+
/** Company/product name shown in header */
|
|
17
|
+
name?: string;
|
|
18
|
+
/** Logo as image URL or React component */
|
|
19
|
+
logo?: string | React.ReactNode;
|
|
20
|
+
/** Avatar shown next to AI messages (URL or React component) */
|
|
21
|
+
avatar?: string | React.ReactNode;
|
|
22
|
+
/** Welcome screen title */
|
|
23
|
+
welcomeTitle?: string;
|
|
24
|
+
/** Welcome screen message */
|
|
25
|
+
welcomeMessage?: string;
|
|
26
|
+
/** Custom welcome screen content (replaces default) */
|
|
27
|
+
welcomeContent?: React.ReactNode;
|
|
28
|
+
/** Show "Powered by Chain AI" badge */
|
|
29
|
+
showPoweredBy?: boolean;
|
|
30
|
+
/** Custom FAB icon when closed */
|
|
31
|
+
fabIcon?: React.ReactNode;
|
|
32
|
+
/** Custom FAB icon when open */
|
|
33
|
+
fabIconOpen?: React.ReactNode;
|
|
34
|
+
/** Custom send button icon */
|
|
35
|
+
sendIcon?: React.ReactNode;
|
|
36
|
+
/** Custom header content (replaces default header) */
|
|
37
|
+
headerContent?: React.ReactNode;
|
|
38
|
+
/** Gradient colors for avatar background [start, end] */
|
|
39
|
+
avatarGradient?: [string, string];
|
|
40
|
+
}
|
|
41
|
+
/** Dimension configuration for the widget */
|
|
42
|
+
export interface DimensionConfig {
|
|
43
|
+
/** FAB button size in pixels */
|
|
44
|
+
fabSize?: number;
|
|
45
|
+
/** Chat window width */
|
|
46
|
+
windowWidth?: number | string;
|
|
47
|
+
/** Chat window height */
|
|
48
|
+
windowHeight?: number | string;
|
|
49
|
+
/** Border radius for FAB */
|
|
50
|
+
fabBorderRadius?: number | string;
|
|
51
|
+
/** Border radius for window */
|
|
52
|
+
windowBorderRadius?: number | string;
|
|
53
|
+
/** Offset from edge (applies to position) */
|
|
54
|
+
offset?: number;
|
|
55
|
+
}
|
|
56
|
+
export interface ChainAIWidgetProps {
|
|
57
|
+
config: ChainAIConfig;
|
|
58
|
+
/** Branding/white-label configuration */
|
|
59
|
+
branding?: BrandingConfig;
|
|
60
|
+
/** Dimension/size configuration */
|
|
61
|
+
dimensions?: DimensionConfig;
|
|
62
|
+
/** Initial open state */
|
|
63
|
+
defaultOpen?: boolean;
|
|
64
|
+
/** Position of the widget */
|
|
65
|
+
position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left';
|
|
66
|
+
/** Custom position (overrides position preset) */
|
|
67
|
+
customPosition?: {
|
|
68
|
+
top?: number | string;
|
|
69
|
+
right?: number | string;
|
|
70
|
+
bottom?: number | string;
|
|
71
|
+
left?: number | string;
|
|
72
|
+
};
|
|
73
|
+
/** Custom theme colors */
|
|
74
|
+
theme?: Partial<Theme>;
|
|
75
|
+
/** Additional CSS class for the container */
|
|
76
|
+
className?: string;
|
|
77
|
+
/** Placeholder text for input */
|
|
78
|
+
placeholder?: string;
|
|
79
|
+
/** Z-index for the widget */
|
|
80
|
+
zIndex?: number;
|
|
81
|
+
/** Called when widget opens/closes */
|
|
82
|
+
onToggle?: (isOpen: boolean) => void;
|
|
83
|
+
/** Called when user sends a message */
|
|
84
|
+
onMessage?: (message: string) => void;
|
|
85
|
+
}
|
|
86
|
+
export declare const ChainAIWidget: React.FC<ChainAIWidgetProps>;
|