@coxwave/tap-sdk 0.0.3 → 0.0.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/README.md +134 -330
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,390 +1,194 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @coxwave/tap-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A vanilla JavaScript SDK for embedding AI chat widgets into web applications with seamless iframe communication.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
- [Installation](#installation)
|
|
8
|
-
- [Quick Use](#quick-use)
|
|
9
|
-
- [Types](#types)
|
|
10
|
-
- [Legacy](#legacy)
|
|
11
|
-
- [License](#license)
|
|
12
|
-
|
|
13
|
-
## installation
|
|
14
|
-
|
|
15
|
-
### npm
|
|
5
|
+
## Installation
|
|
16
6
|
|
|
17
7
|
```bash
|
|
18
|
-
npm
|
|
8
|
+
npm install @coxwave/tap-sdk
|
|
19
9
|
```
|
|
20
10
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
yarn add ax-sdk-chatbot
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
## Quick Use
|
|
28
|
-
|
|
29
|
-
### vite
|
|
30
|
-
|
|
31
|
-
#### Use In Root
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
-main.tsx;
|
|
35
|
-
|
|
36
|
-
import { createRoot } from "react-dom/client";
|
|
11
|
+
## Quick Start
|
|
37
12
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
import App from "./App.tsx";
|
|
41
|
-
|
|
42
|
-
import "./index.css";
|
|
13
|
+
```javascript
|
|
14
|
+
import TapSDK from "@coxwave/tap-sdk";
|
|
43
15
|
|
|
44
16
|
const sdk = new TapSDK({
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
isProd: false,
|
|
17
|
+
pluginKey: "your-plugin-key",
|
|
18
|
+
isProd: true,
|
|
48
19
|
});
|
|
49
20
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
sdk.initChat({
|
|
21
|
+
await sdk.initChat({
|
|
53
22
|
chatApiParams: {
|
|
54
|
-
userId: "
|
|
55
|
-
courseId: "
|
|
56
|
-
courseName: "
|
|
57
|
-
courseCategory: "
|
|
58
|
-
courseSubCategory: "
|
|
59
|
-
clipId:
|
|
60
|
-
clipPlayHead:
|
|
61
|
-
},
|
|
62
|
-
shortcutKey: {
|
|
63
|
-
openChat: { key: "/", modifier: "" },
|
|
64
|
-
sendChat: { key: "Enter", modifier: "" },
|
|
65
|
-
},
|
|
66
|
-
customStyles: {
|
|
67
|
-
floatingButton: {
|
|
68
|
-
isInElement: true,
|
|
69
|
-
parentElementId: "root-button", // <------ here is your Floating Button parent element Id
|
|
70
|
-
},
|
|
23
|
+
userId: "user-123",
|
|
24
|
+
courseId: "course-456",
|
|
25
|
+
courseName: "JavaScript Fundamentals",
|
|
26
|
+
courseCategory: "Programming",
|
|
27
|
+
courseSubCategory: "Frontend",
|
|
28
|
+
clipId: null,
|
|
29
|
+
clipPlayHead: null,
|
|
71
30
|
},
|
|
72
31
|
});
|
|
32
|
+
```
|
|
73
33
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
import { useEffect, useState } from "react";
|
|
77
|
-
|
|
78
|
-
import "./App.css";
|
|
79
|
-
|
|
80
|
-
import TapSDK from "coxwave-sdk";
|
|
81
|
-
|
|
82
|
-
function App({ sdk }: { sdk: TapSDK }) {
|
|
83
|
-
const [clipPlayHead, setClipPlayHead] = useState(0);
|
|
34
|
+
## Features
|
|
84
35
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
36
|
+
- 🎯 **Iframe Communication** - Secure cross-origin messaging with handshake protocol
|
|
37
|
+
- 🎨 **Customizable UI** - Floating button, chat body, and popup styling
|
|
38
|
+
- 📊 **Event System** - Timeline sync, chat events, alarms, and PDF integration
|
|
39
|
+
- 🔔 **Smart Notifications** - Auto-fading alarm system with custom styling
|
|
40
|
+
- 📱 **Responsive Design** - Mobile-friendly with automatic positioning
|
|
41
|
+
- 🚀 **Zero Dependencies** - Self-contained with tree-shaking optimization
|
|
89
42
|
|
|
90
|
-
|
|
91
|
-
}, []);
|
|
43
|
+
## API Reference
|
|
92
44
|
|
|
93
|
-
|
|
94
|
-
sdk.postChatInfo({
|
|
95
|
-
clipId: "58767",
|
|
96
|
-
clipPlayHead,
|
|
97
|
-
});
|
|
98
|
-
}, [clipPlayHead]);
|
|
45
|
+
### Constructor
|
|
99
46
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
<div id="root-button">{Your Floating Button styles Element}</div> // <------ This is your floating button parent element. It must have the same ID as parentElementId.
|
|
103
|
-
</>
|
|
104
|
-
);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export default App;
|
|
47
|
+
```javascript
|
|
48
|
+
new TapSDK(options)
|
|
108
49
|
```
|
|
109
50
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
-
|
|
113
|
-
|
|
114
|
-
```ts
|
|
115
|
-
import { useEffect, useState } from "react";
|
|
51
|
+
**Options:**
|
|
52
|
+
- `pluginKey` (string) - Your unique plugin identifier
|
|
53
|
+
- `isProd` (boolean) - Production mode flag (default: false)
|
|
54
|
+
- `isLocal` (boolean) - Local development flag (default: false)
|
|
116
55
|
|
|
117
|
-
|
|
56
|
+
### Methods
|
|
118
57
|
|
|
119
|
-
|
|
58
|
+
#### `initChat(config)`
|
|
120
59
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
60
|
+
```javascript
|
|
61
|
+
await sdk.initChat({
|
|
62
|
+
chatApiParams: {
|
|
63
|
+
userId: string,
|
|
64
|
+
courseId: string | null,
|
|
65
|
+
courseName: string,
|
|
66
|
+
courseCategory: string,
|
|
67
|
+
courseSubCategory: string,
|
|
68
|
+
clipId: string | null,
|
|
69
|
+
clipPlayHead: number | null
|
|
70
|
+
},
|
|
71
|
+
customStyles?: {
|
|
72
|
+
floatingButton?: {
|
|
73
|
+
isInElement?: boolean,
|
|
74
|
+
parentElementId?: string,
|
|
75
|
+
style?: Partial<CSSStyleDeclaration>
|
|
76
|
+
},
|
|
77
|
+
chatBody?: {
|
|
78
|
+
position?: { top?: string, right?: string, left?: string, bottom?: string },
|
|
79
|
+
width?: string,
|
|
80
|
+
height?: string,
|
|
81
|
+
borderRadius?: string
|
|
82
|
+
}
|
|
83
|
+
}
|
|
125
84
|
});
|
|
85
|
+
```
|
|
126
86
|
|
|
87
|
+
#### `seekTimeline(params)`
|
|
127
88
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
userId: "user_id",
|
|
135
|
-
courseId: "course_id",
|
|
136
|
-
courseName: "course_name",
|
|
137
|
-
courseCategory: "course_category",
|
|
138
|
-
courseSubCategory: "course_sub_category",
|
|
139
|
-
clipId: "clip_id",
|
|
140
|
-
clipPlayHead: clip_play_head_number,
|
|
141
|
-
},
|
|
142
|
-
customStyles: {
|
|
143
|
-
floatingButton: {
|
|
144
|
-
isInElement: true,
|
|
145
|
-
parentElementId: "root-button",
|
|
146
|
-
},
|
|
147
|
-
},
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
return () => {
|
|
151
|
-
sdk.removeChat(); // this is sdk cleanup function
|
|
152
|
-
};
|
|
153
|
-
}, []);
|
|
154
|
-
|
|
155
|
-
useEffect(() => {
|
|
156
|
-
const interval = setInterval(() => {
|
|
157
|
-
setClipPlayHead((prev) => prev + 5);
|
|
158
|
-
}, 1000);
|
|
159
|
-
|
|
160
|
-
return () => clearInterval(interval);
|
|
161
|
-
}, []);
|
|
162
|
-
|
|
163
|
-
useEffect(() => {
|
|
164
|
-
sdk.postChatInfo({
|
|
165
|
-
clipId: "58767",
|
|
166
|
-
clipPlayHead,
|
|
167
|
-
});
|
|
168
|
-
}, [clipPlayHead]);
|
|
169
|
-
|
|
170
|
-
return (
|
|
171
|
-
<S.LayoutContainer $isWhiteBack={true}>
|
|
172
|
-
<S.ContentBox>
|
|
173
|
-
<S.RootButton id="root-button">
|
|
174
|
-
<S.BotImage src={BotImage} />
|
|
175
|
-
<S.Text>AI 튜터</S.Text>
|
|
176
|
-
</S.RootButton>
|
|
177
|
-
</S.ContentBox>
|
|
178
|
-
</S.LayoutContainer>
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export default Test;
|
|
89
|
+
```javascript
|
|
90
|
+
sdk.seekTimeline({
|
|
91
|
+
clipId: 'clip-123',
|
|
92
|
+
clipPlayHead: 150.5
|
|
93
|
+
});
|
|
94
|
+
```
|
|
183
95
|
|
|
184
|
-
|
|
185
|
-
~
|
|
186
|
-
} // your styles (this example is for styled-components)
|
|
96
|
+
#### `removeChat()`
|
|
187
97
|
|
|
98
|
+
```javascript
|
|
99
|
+
sdk.removeChat();
|
|
188
100
|
```
|
|
189
101
|
|
|
190
|
-
###
|
|
102
|
+
### Event Listeners
|
|
191
103
|
|
|
192
|
-
|
|
104
|
+
```javascript
|
|
105
|
+
// Timeline seek events
|
|
106
|
+
sdk.events.onTimelineSeek((clipPlayHead, clipId) => {
|
|
107
|
+
console.log(`Seek to ${clipPlayHead}s in ${clipId}`);
|
|
108
|
+
});
|
|
193
109
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
export default function Home() {
|
|
198
|
-
return (
|
|
199
|
-
<Sdk />
|
|
200
|
-
);
|
|
201
|
-
}
|
|
110
|
+
// Chat state events
|
|
111
|
+
sdk.events.onChatOpened(() => console.log('Chat opened'));
|
|
112
|
+
sdk.events.onChatClosed(() => console.log('Chat closed'));
|
|
202
113
|
|
|
114
|
+
// Notification events
|
|
115
|
+
sdk.events.onAlarmFadeIn((messageInfo) => {
|
|
116
|
+
console.log('Alarm notification:', messageInfo);
|
|
117
|
+
});
|
|
203
118
|
```
|
|
204
119
|
|
|
205
|
-
|
|
120
|
+
## Framework Integration
|
|
206
121
|
|
|
207
|
-
|
|
208
|
-
"use client"; // if you use this sdk in Next.js, you must use this
|
|
122
|
+
### React
|
|
209
123
|
|
|
210
|
-
|
|
124
|
+
```jsx
|
|
125
|
+
import { useEffect, useRef } from 'react';
|
|
126
|
+
import TapSDK from '@coxwave/tap-sdk';
|
|
211
127
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const clientUrl = process.env.NEXT_PUBLIC_HOST_CLIENT_URL; // your client url (if you use localhost, your client url is http://localhost:3000)
|
|
215
|
-
const pluginKey = process.env.NEXT_PUBLIC_COXWAVE_PLUGIN_KEY;
|
|
216
|
-
|
|
217
|
-
if (!clientUrl) {
|
|
218
|
-
throw new Error("NEXT_PUBLIC_COXWAVE_CLIENT_URL is not set");
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
if (!pluginKey) {
|
|
222
|
-
throw new Error("NEXT_PUBLIC_COXWAVE_PLUGIN_KEY is not set");
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
function Sdk() {
|
|
226
|
-
const iframeRef = useRef<HTMLDivElement>(null);
|
|
128
|
+
function ChatWidget({ userId, courseId }) {
|
|
129
|
+
const sdkRef = useRef(null);
|
|
227
130
|
|
|
228
131
|
useEffect(() => {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
await TapSDKInstance.initChat({
|
|
239
|
-
chatApiParams: {
|
|
240
|
-
userId: "user_id",
|
|
241
|
-
courseId: "course_id",
|
|
242
|
-
courseName: "course_name",
|
|
243
|
-
courseCategory: "course_category",
|
|
244
|
-
courseSubCategory: "course_sub_category",
|
|
245
|
-
clipId: "clip_id",
|
|
246
|
-
clipPlayHead: clip_play_head_number,
|
|
247
|
-
},
|
|
248
|
-
customStyles: {
|
|
249
|
-
floatingButton: {
|
|
250
|
-
isInElement: true,
|
|
251
|
-
parentElementId: "root-button", // <------ here is your Floating Button parent element Id
|
|
252
|
-
},
|
|
253
|
-
},
|
|
254
|
-
});
|
|
255
|
-
} catch (err) {
|
|
256
|
-
console.error("SDK initChat 실패:", err);
|
|
257
|
-
}
|
|
132
|
+
const initSDK = async () => {
|
|
133
|
+
sdkRef.current = new TapSDK({
|
|
134
|
+
pluginKey: process.env.REACT_APP_TAP_PLUGIN_KEY,
|
|
135
|
+
isProd: process.env.NODE_ENV === 'production'
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
await sdkRef.current.initChat({
|
|
139
|
+
chatApiParams: { userId, courseId, /* ... */ }
|
|
140
|
+
});
|
|
258
141
|
};
|
|
259
142
|
|
|
260
|
-
|
|
143
|
+
initSDK();
|
|
144
|
+
return () => sdkRef.current?.removeChat();
|
|
145
|
+
}, [userId, courseId]);
|
|
261
146
|
|
|
262
|
-
|
|
263
|
-
TapSDKInstance.removeChat();
|
|
264
|
-
};
|
|
265
|
-
}, [clientUrl]);
|
|
266
|
-
|
|
267
|
-
return (
|
|
268
|
-
<div
|
|
269
|
-
ref={iframeRef}
|
|
270
|
-
id="root-button" // <------ This is your floating button parent element. It must have the same ID as parentElementId.
|
|
271
|
-
style={{
|
|
272
|
-
width: "50px",
|
|
273
|
-
height: "50px",
|
|
274
|
-
backgroundColor: "red",
|
|
275
|
-
cursor: "pointer",
|
|
276
|
-
}}
|
|
277
|
-
>
|
|
278
|
-
<button>Open Chat</button>
|
|
279
|
-
</div>
|
|
280
|
-
);
|
|
147
|
+
return <div id="chat-container" />;
|
|
281
148
|
}
|
|
282
|
-
|
|
283
|
-
export default Sdk;
|
|
284
149
|
```
|
|
285
150
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
### SDK Class
|
|
289
|
-
|
|
290
|
-
```ts
|
|
291
|
-
const TapSDKInstance = new TapSDK({
|
|
292
|
-
hostClientUrl: "~",
|
|
293
|
-
pluginKey: "~",
|
|
294
|
-
});
|
|
151
|
+
### Next.js
|
|
295
152
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
};
|
|
301
|
-
```
|
|
153
|
+
```jsx
|
|
154
|
+
"use client";
|
|
155
|
+
import { useEffect } from 'react';
|
|
156
|
+
import TapSDK from '@coxwave/tap-sdk';
|
|
302
157
|
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
shortcutKey,
|
|
310
|
-
}: {
|
|
311
|
-
chatApiParams: ChatApiParamsType;
|
|
312
|
-
customStyles?: CustomStylesType;
|
|
313
|
-
shortcutKey?: ShortcutKeyType;
|
|
314
|
-
})
|
|
315
|
-
|
|
316
|
-
type ChatApiParamsType = {
|
|
317
|
-
userId: string;
|
|
318
|
-
courseId: string;
|
|
319
|
-
courseName: string;
|
|
320
|
-
courseCategory: string;
|
|
321
|
-
courseSubCategory: string;
|
|
322
|
-
clipId: string | null;
|
|
323
|
-
clipPlayHead: number | null;
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
type CustomStylesType = {
|
|
327
|
-
floatingButton?: FloatingButtonType;
|
|
328
|
-
chatBody?: ChatBodyType;
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
type ShortcutKeyType = {
|
|
332
|
-
openChat: ShortcutKeyPropertiesType;
|
|
333
|
-
sendChat: ShortcutKeyPropertiesType;
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
type FloatingButtonType = {
|
|
337
|
-
isInElement?: boolean;
|
|
338
|
-
parentElementId?: string;
|
|
339
|
-
style?: Partial<CSSStyleDeclaration>;
|
|
340
|
-
};
|
|
341
|
-
|
|
342
|
-
type ChatBodyType = {
|
|
343
|
-
position?: PositionType;
|
|
344
|
-
width?: string;
|
|
345
|
-
height?: string;
|
|
346
|
-
};
|
|
347
|
-
|
|
348
|
-
type PositionType = {
|
|
349
|
-
top?: string;
|
|
350
|
-
left?: string;
|
|
351
|
-
right?: string;
|
|
352
|
-
bottom?: string;
|
|
353
|
-
};
|
|
354
|
-
|
|
355
|
-
type ShortcutKeyPropertiesType = {
|
|
356
|
-
key: string;
|
|
357
|
-
modifier: "ctrlKey" | "altKey" | "shiftKey" | "metaKey" | "";
|
|
358
|
-
};
|
|
158
|
+
export default function ChatPage() {
|
|
159
|
+
useEffect(() => {
|
|
160
|
+
const sdk = new TapSDK({
|
|
161
|
+
pluginKey: process.env.NEXT_PUBLIC_TAP_PLUGIN_KEY,
|
|
162
|
+
isProd: process.env.NODE_ENV === 'production'
|
|
163
|
+
});
|
|
359
164
|
|
|
360
|
-
|
|
165
|
+
sdk.initChat({
|
|
166
|
+
chatApiParams: { /* ... */ }
|
|
167
|
+
});
|
|
361
168
|
|
|
362
|
-
|
|
169
|
+
return () => sdk.removeChat();
|
|
170
|
+
}, []);
|
|
363
171
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
clipId,
|
|
367
|
-
clipPlayHead,
|
|
368
|
-
}: {
|
|
369
|
-
clipId: string;
|
|
370
|
-
clipPlayHead: number;
|
|
371
|
-
})
|
|
172
|
+
return <div>Chat Widget will appear here</div>;
|
|
173
|
+
}
|
|
372
174
|
```
|
|
373
175
|
|
|
374
|
-
|
|
176
|
+
## Build Formats
|
|
375
177
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
```
|
|
178
|
+
- **ESM**: `dist/index.mjs` - Modern ES modules
|
|
179
|
+
- **CJS**: `dist/index.js` - CommonJS for Node.js
|
|
180
|
+
- **IIFE**: `dist/index.global.js` - Browser global variable
|
|
181
|
+
- **Types**: `dist/index.d.ts` - TypeScript declarations
|
|
381
182
|
|
|
382
|
-
##
|
|
183
|
+
## Browser Support
|
|
383
184
|
|
|
384
|
-
|
|
185
|
+
- Chrome 60+
|
|
186
|
+
- Firefox 60+
|
|
187
|
+
- Safari 12+
|
|
188
|
+
- Edge 79+
|
|
385
189
|
|
|
386
|
-
##
|
|
190
|
+
## License
|
|
387
191
|
|
|
388
|
-
|
|
192
|
+
MIT
|
|
389
193
|
|
|
390
|
-
Copyright (c) 2025-present, Coxwave
|
|
194
|
+
Copyright (c) 2025-present, Coxwave
|