@coxwave/tap-sdk 0.0.3 → 0.0.5

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 CHANGED
@@ -1,390 +1,170 @@
1
- # ax-sdk-chatbot
1
+ # @coxwave/tap-sdk
2
2
 
3
- ## version: 1.1.0
3
+ A vanilla JavaScript SDK for embedding AI chat widgets into web applications with seamless iframe communication.
4
4
 
5
- ## 📚 Table of Contents
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 i ax-sdk-chatbot
8
+ npm install @coxwave/tap-sdk
19
9
  ```
20
10
 
21
- ### yarn
22
-
23
- ```bash
24
- yarn add ax-sdk-chatbot
25
- ```
26
-
27
- ## Quick Use
28
-
29
- ### vite
30
-
31
- #### Use In Root
11
+ ## Quick Start
32
12
 
33
- ```ts
34
- -main.tsx;
35
-
36
- import { createRoot } from "react-dom/client";
37
-
38
- import TapSDK from "ax-sdk-chatbot";
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
- hostClientUrl: import.meta.env.VITE_CLIENT_URL, // your client url (if you use localhost, your client url is http://localhost:5173)
46
- pluginKey: import.meta.env.VITE_FIRSTBRAIN_PLUGIN_KEY,
47
- isProd: false,
17
+ pluginKey: "your-plugin-key",
18
+ isProd: true,
48
19
  });
49
20
 
50
- createRoot(document.getElementById("root")!).render(<App />);
51
-
52
- sdk.initChat({
21
+ await sdk.initChat({
53
22
  chatApiParams: {
54
- userId: "user_id",
55
- courseId: "course_id",
56
- courseName: "course_name",
57
- courseCategory: "course_category",
58
- courseSubCategory: "course_sub_category",
59
- clipId: "clip_id",
60
- clipPlayHead: clip_play_head_number,
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
- -App.tsx;
75
-
76
- import { useEffect, useState } from "react";
77
-
78
- import "./App.css";
34
+ ## Features
79
35
 
80
- import TapSDK from "coxwave-sdk";
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
81
42
 
82
- function App({ sdk }: { sdk: TapSDK }) {
83
- const [clipPlayHead, setClipPlayHead] = useState(0);
43
+ ## API Reference
84
44
 
85
- useEffect(() => {
86
- const interval = setInterval(() => {
87
- setClipPlayHead((prev) => prev + 5);
88
- }, 1000);
45
+ ### Constructor
89
46
 
90
- return () => clearInterval(interval);
91
- }, []);
92
-
93
- useEffect(() => {
94
- sdk.postChatInfo({
95
- clipId: "58767",
96
- clipPlayHead,
97
- });
98
- }, [clipPlayHead]);
99
-
100
- return (
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
- #### Use Custom Hook (with useEffect)
51
+ **Options:**
111
52
 
112
- - Test.tsx (can use any component)
53
+ - `pluginKey` (string) - Your unique plugin identifier
54
+ - `isProd` (boolean) - Production mode flag (default: false)
55
+ - `isLocal` (boolean) - Local development flag (default: false)
113
56
 
114
- ```ts
115
- import { useEffect, useState } from "react";
57
+ ### Methods
116
58
 
117
- import TapSDK from "ax-sdk-chatbot";
59
+ #### `initChat(config)`
118
60
 
119
- import BotImage from "./assets/quick-menu.png";
120
-
121
- const sdk = new TapSDK({
122
- hostClientUrl: import.meta.env.VITE_CLIENT_URL,
123
- pluginKey: import.meta.env.VITE_FIRSTBRAIN_PLUGIN_KEY,
124
- isProd: import.meta.env.VITE_IS_PROD === "true",
61
+ ```javascript
62
+ await sdk.initChat({
63
+ chatApiParams: {
64
+ userId: string,
65
+ courseId: string | null,
66
+ courseName: string,
67
+ courseCategory: string,
68
+ courseSubCategory: string,
69
+ clipId: string | null,
70
+ clipPlayHead: number | null
71
+ },
72
+ customStyles?: {
73
+ floatingButton?: {
74
+ isInElement?: boolean,
75
+ parentElementId?: string,
76
+ style?: Partial<CSSStyleDeclaration>
77
+ },
78
+ chatBody?: {
79
+ position?: { top?: string, right?: string, left?: string, bottom?: string },
80
+ width?: string,
81
+ height?: string,
82
+ borderRadius?: string
83
+ }
84
+ }
125
85
  });
86
+ ```
126
87
 
88
+ #### `seekTimeline(params)`
127
89
 
128
- function Test() {
129
- const [clipPlayHead, setClipPlayHead] = useState(0);
130
-
131
- useEffect(() => {
132
- sdk.initChat({
133
- chatApiParams: {
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;
90
+ ```javascript
91
+ sdk.seekTimeline({
92
+ clipId: "clip-123",
93
+ clipPlayHead: 150.5,
94
+ });
95
+ ```
183
96
 
184
- const S = {
185
- ~
186
- } // your styles (this example is for styled-components)
97
+ #### `removeChat()`
187
98
 
99
+ ```javascript
100
+ sdk.removeChat();
188
101
  ```
189
102
 
190
- ### Next.js
191
-
192
- - page.tsx
103
+ ### Event Listeners
193
104
 
194
- ```ts
195
- import Sdk from "./Sdk";
105
+ ```javascript
106
+ // Timeline seek events
107
+ sdk.events.onTimelineSeek((clipPlayHead, clipId) => {
108
+ console.log(`Seek to ${clipPlayHead}s in ${clipId}`);
109
+ });
196
110
 
197
- export default function Home() {
198
- return (
199
- <Sdk />
200
- );
201
- }
111
+ // Chat state events
112
+ sdk.events.onChatOpened(() => console.log("Chat opened"));
113
+ sdk.events.onChatClosed(() => console.log("Chat closed"));
202
114
 
115
+ // Notification events
116
+ sdk.events.onAlarmFadeIn((messageInfo) => {
117
+ console.log("Alarm notification:", messageInfo);
118
+ });
203
119
  ```
204
120
 
205
- - Sdk.tsx
121
+ ## Framework Integration
206
122
 
207
- ```ts
208
- "use client"; // if you use this sdk in Next.js, you must use this
123
+ ### React
209
124
 
125
+ ```jsx
210
126
  import { useEffect, useRef } from "react";
127
+ import TapSDK from "@coxwave/tap-sdk";
211
128
 
212
- import TapSDK from "ax-sdk-chatbot";
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);
129
+ function ChatWidget({ userId, courseId }) {
130
+ const sdkRef = useRef(null);
227
131
 
228
132
  useEffect(() => {
229
- if (!clientUrl || !pluginKey) return;
230
-
231
- const TapSDKInstance = new TapSDK({
232
- hostClientUrl: clientUrl,
233
- pluginKey: pluginKey,
234
- });
235
-
236
- const init = async () => {
237
- try {
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
- }
133
+ const initSDK = async () => {
134
+ sdkRef.current = new TapSDK({
135
+ pluginKey: process.env.REACT_APP_TAP_PLUGIN_KEY,
136
+ isProd: process.env.NODE_ENV === "production",
137
+ });
138
+
139
+ await sdkRef.current.initChat({
140
+ chatApiParams: { userId, courseId /* ... */ },
141
+ });
258
142
  };
259
143
 
260
- init(); // 즉시 실행
144
+ initSDK();
145
+ return () => sdkRef.current?.removeChat();
146
+ }, [userId, courseId]);
261
147
 
262
- return () => {
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
- );
148
+ return <div id="chat-container" />;
281
149
  }
282
-
283
- export default Sdk;
284
- ```
285
-
286
- ## Types
287
-
288
- ### SDK Class
289
-
290
- ```ts
291
- const TapSDKInstance = new TapSDK({
292
- hostClientUrl: "~",
293
- pluginKey: "~",
294
- });
295
-
296
- type TapSDKType = {
297
- hostClientUrl: string;
298
- pluginKey: string;
299
- isProd?: boolean;
300
- };
301
150
  ```
302
151
 
303
- ### initChat Method
304
-
305
- ```ts
306
- async initChat({
307
- chatApiParams,
308
- customStyles,
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
- };
152
+ ## Build Formats
359
153
 
360
- ```
361
-
362
- ### postChatInfo Method
363
-
364
- ```ts
365
- postChatInfo({
366
- clipId,
367
- clipPlayHead,
368
- }: {
369
- clipId: string;
370
- clipPlayHead: number;
371
- })
372
- ```
373
-
374
- - you must use postChatInfo method after initChat method
375
-
376
- ### removeChat Method
377
-
378
- ```ts
379
- removeChat();
380
- ```
154
+ - **ESM**: `dist/index.mjs` - Modern ES modules
155
+ - **CJS**: `dist/index.js` - CommonJS for Node.js
156
+ - **IIFE**: `dist/index.global.js` - Browser global variable
157
+ - **Types**: `dist/index.d.ts` - TypeScript declarations
381
158
 
382
- ## legacy
159
+ ## Browser Support
383
160
 
384
- Ver 0.x.x: https://www.npmjs.com/package/ax-sdk-chatbot/v/0.3.7
161
+ - Chrome 60+
162
+ - Firefox 60+
163
+ - Safari 12+
164
+ - Edge 79+
385
165
 
386
- ## license
166
+ ## License
387
167
 
388
- [MIT](https://opensource.org/licenses/MIT)
168
+ MIT
389
169
 
390
170
  Copyright (c) 2025-present, Coxwave
package/dist/index.d.cts CHANGED
@@ -40,7 +40,7 @@ declare class TapIframeBridge extends Messenger<ToTapMessage, FromTapMessage> {
40
40
  hostClientUrl: string;
41
41
  pluginKey: string;
42
42
  });
43
- protected isValidOrigin(event: MessageEvent): boolean;
43
+ protected isValidOrigin(_event: MessageEvent): boolean;
44
44
  protected getMessageTarget(): Window | null;
45
45
  protected getTargetOrigin(): string;
46
46
  renderIframe({ chatBody, isProd, isLocal, }: {
@@ -50,7 +50,7 @@ declare class TapIframeBridge extends Messenger<ToTapMessage, FromTapMessage> {
50
50
  }): Promise<HTMLIFrameElement>;
51
51
  hasIframe(): boolean;
52
52
  removeIframe(): void;
53
- get postToTap(): (message: ToTapMessage) => boolean;
53
+ get postToTap(): (() => void) | ((message: ToTapMessage) => boolean);
54
54
  listenToTap: <T extends "timeline:seek" | "chat:opened" | "chat:initiated" | "chat:closed" | "alarm:fadeIn" | "popUp:open" | "pdf:open" | "pdf:close">(messageType: T, callback: (data: Extract<TimelineSeekMessage, {
55
55
  type: T;
56
56
  }> | Extract<ChatInitiatedMessage, {
package/dist/index.d.ts CHANGED
@@ -40,7 +40,7 @@ declare class TapIframeBridge extends Messenger<ToTapMessage, FromTapMessage> {
40
40
  hostClientUrl: string;
41
41
  pluginKey: string;
42
42
  });
43
- protected isValidOrigin(event: MessageEvent): boolean;
43
+ protected isValidOrigin(_event: MessageEvent): boolean;
44
44
  protected getMessageTarget(): Window | null;
45
45
  protected getTargetOrigin(): string;
46
46
  renderIframe({ chatBody, isProd, isLocal, }: {
@@ -50,7 +50,7 @@ declare class TapIframeBridge extends Messenger<ToTapMessage, FromTapMessage> {
50
50
  }): Promise<HTMLIFrameElement>;
51
51
  hasIframe(): boolean;
52
52
  removeIframe(): void;
53
- get postToTap(): (message: ToTapMessage) => boolean;
53
+ get postToTap(): (() => void) | ((message: ToTapMessage) => boolean);
54
54
  listenToTap: <T extends "timeline:seek" | "chat:opened" | "chat:initiated" | "chat:closed" | "alarm:fadeIn" | "popUp:open" | "pdf:open" | "pdf:close">(messageType: T, callback: (data: Extract<TimelineSeekMessage, {
55
55
  type: T;
56
56
  }> | Extract<ChatInitiatedMessage, {