@action-x/ad-sdk 0.1.11 → 0.1.13

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,185 +1,385 @@
1
- # @action-x/ad-sdk
1
+ # @action-x/ad-sdk Usage Guide
2
2
 
3
- An ad SDK for AI conversation experiences, with zero framework dependencies. Works with Vanilla JS, Vue, React, Angular, Svelte, and more.
3
+ Zero-dependency ad SDK for AI conversations, supporting Vanilla JS, Vue, React, Next.js, and all frameworks.
4
4
 
5
5
  ---
6
6
 
7
- ## Installation
7
+ ## Quick Start
8
+
9
+ ### Installation
8
10
 
9
11
  ```bash
10
12
  npm install @action-x/ad-sdk
11
- # or
12
- yarn add @action-x/ad-sdk
13
- # or
14
- pnpm add @action-x/ad-sdk
15
13
  ```
16
14
 
17
- **Import styles (required):**
15
+ ### Minimal Example
18
16
 
19
- ```js
17
+ ```typescript
18
+ import { AdManager } from '@action-x/ad-sdk';
20
19
  import '@action-x/ad-sdk/style.css';
20
+
21
+ const manager = new AdManager({
22
+ apiBaseUrl: 'https://network.actionx.top/api/v1',
23
+ apiKey: 'ak_your_api_key',
24
+ });
25
+
26
+ // Request ads after AI conversation completes
27
+ await manager.requestAds({
28
+ query: 'Recommend a Bluetooth headset',
29
+ response: 'AI response content...',
30
+ });
31
+
32
+ // Render ad
33
+ manager.render(document.getElementById('ad-container'));
21
34
  ```
22
35
 
23
36
  ---
24
37
 
25
- ## AdCard Component
38
+ ## Core Concepts
26
39
 
27
- Below are AdCard wrapper examples for different frameworks. The component takes `query` and `response` from the AI conversation, then automatically requests and renders an ad card after the response is complete.
40
+ ### 1. AdManager Configuration
28
41
 
29
- ### Vue 3
42
+ ```typescript
43
+ const manager = new AdManager({
44
+ apiBaseUrl: string; // Required: API endpoint
45
+ apiKey: string; // Required: API key
46
+ cardOption?: { // Optional: Card configuration
47
+ variant?: 'horizontal' | 'vertical' | 'compact'; // Layout style
48
+ count?: number; // Number of ads to request, default 1
49
+ };
50
+ enabled?: boolean; // Enable ads, default true
51
+ debug?: boolean; // Debug mode, default false
52
+ });
53
+ ```
30
54
 
31
- ```vue
32
- <!-- AdCard.vue -->
33
- <template>
34
- <div ref="adEl" />
35
- </template>
55
+ ### 2. Request Ads
36
56
 
37
- <script setup>
38
- import { ref, onMounted, onUnmounted } from 'vue';
39
- import { AdManager } from '@action-x/ad-sdk';
40
- import '@action-x/ad-sdk/style.css';
57
+ ```typescript
58
+ // Simplified call (recommended)
59
+ await manager.requestAds({
60
+ query: string; // Required: User query
61
+ response: string; // Required: AI response content
62
+ });
41
63
 
42
- const props = defineProps(['query', 'response']);
43
- const adEl = ref(null);
44
- let manager;
64
+ // Full call
65
+ await manager.requestAds({
66
+ conversationContext: {
67
+ query: string;
68
+ response: string;
69
+ },
70
+ userContext?: {
71
+ sessionId?: string;
72
+ userId?: string;
73
+ }
74
+ });
75
+ ```
45
76
 
46
- onMounted(async () => {
47
- manager = new AdManager({
48
- apiBaseUrl: 'https://your-api.example.com/api/v1',
49
- apiKey: 'ak_your_api_key',
50
- });
77
+ ### 3. Render Ads
78
+
79
+ ```typescript
80
+ // Simplified call (renders action_card slot by default)
81
+ manager.render(
82
+ container: HTMLElement,
83
+ options?: {
84
+ adIndex?: number; // Ad index, default 0
85
+ variant?: string; // Layout variant
86
+ onClick?: (ad) => void; // Click callback
87
+ onImpression?: (ad) => void; // Impression callback
88
+ }
89
+ );
90
+
91
+ // Specify slot
92
+ manager.render(
93
+ slotId: string,
94
+ container: HTMLElement,
95
+ options?: { ... }
96
+ );
97
+ ```
51
98
 
52
- await manager.requestAds({ query: props.query, response: props.response });
99
+ ### 4. Event Listeners
53
100
 
54
- manager.render(adEl.value);
101
+ ```typescript
102
+ manager.on('adsUpdated', (slots) => {
103
+ console.log('Ad data updated');
55
104
  });
56
105
 
57
- onUnmounted(() => manager?.destroy());
58
- </script>
106
+ manager.on('adsLoading', () => {
107
+ console.log('Requesting ads');
108
+ });
109
+
110
+ manager.on('adsError', (error) => {
111
+ console.warn('Ad request failed', error);
112
+ });
113
+
114
+ manager.on('adClicked', (adId, slotId) => {
115
+ console.log('Ad clicked', adId);
116
+ });
117
+
118
+ manager.on('adImpression', (adId, slotId) => {
119
+ console.log('Ad impression', adId);
120
+ });
59
121
  ```
60
122
 
61
- ### React
123
+ ---
62
124
 
63
- ```tsx
64
- // components/AdCard.tsx
65
- import { useEffect, useRef } from 'react';
66
- import { AdManager } from '@action-x/ad-sdk';
67
- import '@action-x/ad-sdk/style.css';
125
+ ## Framework Integration Examples
68
126
 
69
- interface Props {
70
- query: string;
71
- response: string;
72
- }
127
+ ### Vanilla JavaScript
73
128
 
74
- export function AdCard({ query, response }: Props) {
75
- const containerRef = useRef<HTMLDivElement>(null);
129
+ ```html
130
+ <!DOCTYPE html>
131
+ <html>
132
+ <head>
133
+ <link rel="stylesheet" href="https://unpkg.com/@action-x/ad-sdk@latest/dist/style.css" />
134
+ </head>
135
+ <body>
136
+ <div id="ad-container"></div>
137
+
138
+ <script src="https://unpkg.com/@action-x/ad-sdk@latest/dist/index.umd.js"></script>
139
+ <script>
140
+ const { AdManager } = ActionXAdSDK;
76
141
 
77
- useEffect(() => {
78
142
  const manager = new AdManager({
79
- apiBaseUrl: 'https://your-api.example.com/api/v1',
143
+ apiBaseUrl: 'https://network.actionx.top/api/v1',
80
144
  apiKey: 'ak_your_api_key',
145
+ debug: false,
81
146
  });
82
147
 
83
- manager
84
- .requestAds({ query, response })
85
- .then(() => {
86
- if (containerRef.current) {
87
- manager.render(containerRef.current);
88
- }
89
- });
148
+ manager.on('adsUpdated', () => {
149
+ manager.render(document.getElementById('ad-container'));
150
+ });
90
151
 
91
- return () => manager.destroy();
92
- }, [query, response]);
152
+ // Call after conversation completes
153
+ function onChatComplete(query, response) {
154
+ manager.requestAds({ query, response });
155
+ }
156
+ </script>
157
+ </body>
158
+ </html>
159
+ ```
93
160
 
94
- return <div ref={containerRef} />;
95
- }
161
+ ### Vue 3
162
+
163
+ **Install and import styles:**
164
+
165
+ ```typescript
166
+ // main.ts
167
+ import { createApp } from 'vue';
168
+ import App from './App.vue';
169
+ import '@action-x/ad-sdk/style.css';
170
+
171
+ createApp(App).mount('#app');
96
172
  ```
97
173
 
98
- ### Vanilla JavaScript
174
+ **Create Composable:**
99
175
 
100
- ```js
176
+ ```typescript
177
+ // composables/useAdManager.ts
178
+ import { ref, shallowRef, onUnmounted } from 'vue';
101
179
  import { AdManager } from '@action-x/ad-sdk';
102
- import '@action-x/ad-sdk/style.css';
103
180
 
104
- const manager = new AdManager({
105
- apiBaseUrl: 'https://your-api.example.com/api/v1',
106
- apiKey: 'ak_your_api_key',
107
- });
181
+ export function useAdManager() {
182
+ const manager = shallowRef(
183
+ new AdManager({
184
+ apiBaseUrl: 'https://network.actionx.top/api/v1',
185
+ apiKey: 'ak_your_api_key',
186
+ debug: false,
187
+ })
188
+ );
108
189
 
109
- // Request and render ads after the AI response is complete
110
- await manager.requestAds({
111
- query: 'Recommend a Bluetooth headset',
112
- response: 'Here are several popular Bluetooth headset options...',
113
- });
190
+ const isLoading = ref(false);
191
+ const error = ref<Error | null>(null);
192
+
193
+ manager.value.on('adsLoading', () => {
194
+ isLoading.value = true;
195
+ error.value = null;
196
+ });
197
+
198
+ manager.value.on('adsUpdated', () => {
199
+ isLoading.value = false;
200
+ });
201
+
202
+ manager.value.on('adsError', (e) => {
203
+ isLoading.value = false;
204
+ error.value = e;
205
+ });
206
+
207
+ onUnmounted(() => manager.value.destroy());
114
208
 
115
- manager.render(document.getElementById('ad-card'));
209
+ async function requestAds(query: string, response: string) {
210
+ await manager.value.requestAds({ query, response });
211
+ }
212
+
213
+ return { manager, isLoading, error, requestAds };
214
+ }
116
215
  ```
117
216
 
118
- ### CDN (No Build Tool)
217
+ **Use in component:**
119
218
 
120
- ```html
121
- <link rel="stylesheet" href="https://unpkg.com/@action-x/ad-sdk/style.css" />
122
- <script src="https://unpkg.com/@action-x/ad-sdk/dist/index.umd.js"></script>
219
+ ```vue
220
+ <template>
221
+ <div class="chat-response">
222
+ <div class="response-text">{{ responseText }}</div>
223
+ <div ref="adRef" class="ad-mount"></div>
224
+ </div>
225
+ </template>
123
226
 
124
- <div id="ad-card"></div>
227
+ <script setup lang="ts">
228
+ import { ref, watch, nextTick } from 'vue';
229
+ import { useAdManager } from '@/composables/useAdManager';
125
230
 
126
- <script>
127
- const { AdManager } = ActionXAdSDK;
231
+ const props = defineProps<{
232
+ query: string;
233
+ responseText: string;
234
+ isStreaming: boolean;
235
+ }>();
236
+
237
+ const adRef = ref<HTMLElement>();
238
+ const { manager, requestAds } = useAdManager();
239
+
240
+ watch(
241
+ () => props.isStreaming,
242
+ async (streaming) => {
243
+ if (!streaming && props.responseText) {
244
+ await requestAds(props.query, props.responseText);
245
+ await nextTick();
246
+
247
+ if (adRef.value) {
248
+ manager.value.render(adRef.value);
249
+ }
250
+ }
251
+ }
252
+ );
253
+ </script>
254
+ ```
128
255
 
129
- const manager = new AdManager({
130
- apiBaseUrl: 'https://your-api.example.com/api/v1',
131
- apiKey: 'ak_your_api_key',
132
- });
256
+ ### React / Next.js
257
+
258
+ **Import styles:**
259
+
260
+ ```typescript
261
+ // Next.js App Router: app/layout.tsx
262
+ import '@action-x/ad-sdk/style.css';
263
+
264
+ export default function RootLayout({ children }) {
265
+ return <html><body>{children}</body></html>;
266
+ }
267
+
268
+ // Vite + React: src/main.tsx
269
+ import '@action-x/ad-sdk/style.css';
270
+ import React from 'react';
271
+ import ReactDOM from 'react-dom/client';
272
+ import App from './App';
273
+
274
+ ReactDOM.createRoot(document.getElementById('root')!).render(<App />);
275
+ ```
276
+
277
+ **Create Hook:**
278
+
279
+ ```typescript
280
+ // hooks/useAdManager.ts
281
+ import { useEffect, useRef, useState, useCallback } from 'react';
282
+ import { AdManager } from '@action-x/ad-sdk';
283
+
284
+ export function useAdManager() {
285
+ const managerRef = useRef<AdManager | null>(null);
286
+ const [isLoading, setIsLoading] = useState(false);
287
+ const [error, setError] = useState<Error | null>(null);
288
+ const [isReady, setIsReady] = useState(false);
133
289
 
134
- manager
135
- .requestAds({ query: 'User question', response: 'AI response' })
136
- .then(() => {
137
- manager.render(document.getElementById('ad-card'));
290
+ useEffect(() => {
291
+ const manager = new AdManager({
292
+ apiBaseUrl: 'https://network.actionx.top/api/v1',
293
+ apiKey: 'ak_your_api_key',
294
+ debug: false,
138
295
  });
139
- </script>
296
+
297
+ manager.on('adsLoading', () => setIsLoading(true));
298
+ manager.on('adsUpdated', () => {
299
+ setIsLoading(false);
300
+ setIsReady(true);
301
+ });
302
+ manager.on('adsError', (e) => {
303
+ setIsLoading(false);
304
+ setError(e);
305
+ });
306
+
307
+ managerRef.current = manager;
308
+ return () => manager.destroy();
309
+ }, []);
310
+
311
+ const requestAds = useCallback(async (query: string, response: string) => {
312
+ if (!managerRef.current) return;
313
+ await managerRef.current.requestAds({ query, response });
314
+ }, []);
315
+
316
+ return { manager: managerRef.current, isLoading, isReady, error, requestAds };
317
+ }
140
318
  ```
141
319
 
142
- ---
320
+ **Create Ad Component:**
143
321
 
144
- ## Usage Example
322
+ ```tsx
323
+ // components/AdCard.tsx
324
+ 'use client';
145
325
 
146
- ### Render an Ad Card After AI Response Completes
326
+ import { useEffect, useRef } from 'react';
327
+ import type { AdManager } from '@action-x/ad-sdk';
147
328
 
148
- Using React as an example, render AdCard after streaming output finishes:
329
+ interface AdCardProps {
330
+ manager: AdManager | null;
331
+ isReady: boolean;
332
+ className?: string;
333
+ }
334
+
335
+ export function AdCard({ manager, isReady, className }: AdCardProps) {
336
+ const containerRef = useRef<HTMLDivElement>(null);
337
+
338
+ useEffect(() => {
339
+ if (!isReady || !manager || !containerRef.current) return;
340
+ manager.render(containerRef.current);
341
+ }, [isReady, manager]);
342
+
343
+ return <div ref={containerRef} className={className} />;
344
+ }
345
+ ```
346
+
347
+ **Use in page:**
149
348
 
150
349
  ```tsx
350
+ // app/chat/page.tsx
351
+ 'use client';
352
+
151
353
  import { useState } from 'react';
152
- import { AdCard } from './components/AdCard';
354
+ import { useAdManager } from '@/hooks/useAdManager';
355
+ import { AdCard } from '@/components/AdCard';
153
356
 
154
- export function ChatMessage() {
357
+ export default function ChatPage() {
155
358
  const [query, setQuery] = useState('');
156
359
  const [response, setResponse] = useState('');
157
360
  const [isStreaming, setIsStreaming] = useState(false);
158
361
 
159
- async function handleSend(userQuery: string) {
160
- setQuery(userQuery);
161
- setIsStreaming(true);
162
- setResponse('');
163
-
164
- // Receive AI response as a stream
165
- const stream = await fetchAIResponse(userQuery);
166
- let fullResponse = '';
167
- for await (const chunk of stream) {
168
- fullResponse += chunk;
169
- setResponse(fullResponse);
170
- }
362
+ const { manager, isReady, requestAds } = useAdManager();
171
363
 
364
+ const handleSend = async () => {
365
+ setIsStreaming(true);
366
+ const aiResponse = await fetchAIResponse(query);
367
+ setResponse(aiResponse);
172
368
  setIsStreaming(false);
173
- // After response completes, AdCard will request and render automatically
174
- }
369
+
370
+ await requestAds(query, aiResponse);
371
+ };
175
372
 
176
373
  return (
177
374
  <div>
178
- <div className="response">{response}</div>
179
-
180
- {/* Show ad card after response streaming ends */}
181
- {!isStreaming && response && (
182
- <AdCard query={query} response={response} />
375
+ <input value={query} onChange={(e) => setQuery(e.target.value)} />
376
+ <button onClick={handleSend}>Send</button>
377
+
378
+ {response && (
379
+ <div>
380
+ <p>{response}</p>
381
+ <AdCard manager={manager} isReady={isReady} className="mt-4" />
382
+ </div>
183
383
  )}
184
384
  </div>
185
385
  );
@@ -188,63 +388,146 @@ export function ChatMessage() {
188
388
 
189
389
  ---
190
390
 
191
- ## Configuration
391
+ ## Custom Styles
392
+
393
+ The SDK uses the `ax-ad-*` namespace. Override styles via CSS:
394
+
395
+ ```css
396
+ /* Custom CTA button */
397
+ .ax-ad-cta {
398
+ background-color: #7c3aed !important;
399
+ border-radius: 20px;
400
+ }
401
+
402
+ .ax-ad-cta:hover {
403
+ background-color: #6d28d9 !important;
404
+ }
405
+
406
+ /* Custom card style */
407
+ .ax-ad-card {
408
+ border: none;
409
+ border-radius: 12px;
410
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
411
+ }
192
412
 
193
- ### `new AdManager(config)`
413
+ /* Dark mode */
414
+ @media (prefers-color-scheme: dark) {
415
+ .ax-ad-card {
416
+ background: #1f2937;
417
+ border-color: #374151;
418
+ color: #f9fafb;
419
+ }
420
+ .ax-ad-title { color: #f9fafb; }
421
+ .ax-ad-body { color: #9ca3af; }
422
+ }
194
423
 
195
- | Parameter | Type | Required | Description |
196
- |------|------|------|------|
197
- | `apiBaseUrl` | `string` | Yes | API base URL, e.g. `https://your-api.example.com/api/v1` |
198
- | `apiKey` | `string` | Yes | API authentication key |
199
- | `cardOption` | `CardOption` | No | Card options with sensible defaults |
424
+ /* Hide on mobile */
425
+ @media (max-width: 640px) {
426
+ .ax-ad-card { display: none; }
427
+ }
428
+ ```
200
429
 
201
- ### Card Options `cardOption`
430
+ ---
202
431
 
203
- | Parameter | Type | Default | Description |
204
- |------|------|--------|------|
205
- | `variant` | `'horizontal' \| 'vertical' \| 'compact'` | `'horizontal'` | Layout style |
206
- | `count` | `number` | `1` | Number of ads to request |
432
+ ## Debugging Tips
207
433
 
208
- **Customization example:**
434
+ ### Enable Debug Logging
209
435
 
210
- ```js
211
- new AdManager({
436
+ ```typescript
437
+ const manager = new AdManager({
212
438
  apiBaseUrl: '...',
213
439
  apiKey: '...',
214
- cardOption: {
215
- variant: 'vertical',
216
- count: 2,
217
- },
440
+ debug: true, // Outputs detailed logs to console
218
441
  });
219
442
  ```
220
443
 
221
- ### `requestAds(context)`
444
+ ### Check Ad Response
222
445
 
223
- | Parameter | Type | Required | Description |
224
- |------|------|------|------|
225
- | `query` | `string` | Yes | User question |
226
- | `response` | `string` | Yes | AI response text |
446
+ ```typescript
447
+ manager.on('adsUpdated', (slots) => {
448
+ console.log('Ad slots:', Object.keys(slots));
227
449
 
228
- ### `render(container, options?)`
450
+ Object.entries(slots).forEach(([slotId, slot]) => {
451
+ console.log(`[${slotId}] Status:`, slot.status);
452
+ console.log(`[${slotId}] Ad count:`, slot.ads.length);
229
453
 
230
- | Parameter | Type | Description |
231
- |------|------|------|
232
- | `container` | `HTMLElement` | Mount container |
233
- | `options.onClick` | `(ad) => void` | Click callback |
234
- | `options.onImpression` | `(ad) => void` | Impression callback |
454
+ if (slot.status === 'no_fill') {
455
+ console.log(' → No fill (insufficient inventory or targeting mismatch)');
456
+ }
457
+ if (slot.status === 'error') {
458
+ console.error(' → Slot error:', slot.error);
459
+ }
460
+ });
461
+ });
462
+ ```
463
+
464
+ ### Common Issues
465
+
466
+ | Issue | Possible Cause | Solution |
467
+ |-------|---------------|----------|
468
+ | Empty ad container | `render()` called before `requestAds()` completes | Call `render()` inside `adsUpdated` event |
469
+ | Styles not applied | Forgot to import CSS | Ensure `import '@action-x/ad-sdk/style.css'` |
470
+ | Console 403 error | Invalid or missing apiKey | Check apiKey configuration |
471
+ | TypeScript type errors | Type definitions not generated | Run `npm run build` |
472
+ | Ad status 'no_fill' | No matching ads | Expected behavior, check `manager.hasAds()` before rendering |
473
+ | Clicks unresponsive | Container obscured | Check CSS z-index and pointer-events |
235
474
 
236
475
  ---
237
476
 
238
- ## Event Listeners
477
+ ## API Reference
478
+
479
+ ### AdManager
480
+
481
+ #### Constructor
239
482
 
240
- ```js
241
- manager.on('adsUpdated', (slots) => { /* ad data ready */ });
242
- manager.on('adsLoading', () => { /* requesting */ });
243
- manager.on('adsError', (err) => { /* request failed */ });
244
- manager.on('adClicked', (adId, slotId) => { });
245
- manager.on('adImpression', (adId, slotId) => { });
483
+ ```typescript
484
+ new AdManager(config: AdManagerConfig)
246
485
  ```
247
486
 
487
+ #### Methods
488
+
489
+ ```typescript
490
+ // Request ads
491
+ requestAds(context: { query: string; response: string }): Promise<AdResponseBatch>
492
+ requestAds(context: { conversationContext, userContext? }): Promise<AdResponseBatch>
493
+
494
+ // Render ads
495
+ render(container: HTMLElement, options?: RenderOptions): HTMLElement | null
496
+ render(slotId: string, container: HTMLElement, options?: RenderOptions): HTMLElement | null
497
+
498
+ // Check if ads available
499
+ hasAds(slotId: string): boolean
500
+
501
+ // Get ad data
502
+ getAds(slotId: string): any[]
503
+ getSlots(slotId?: string): Record<string, AdSlotResponse> | AdSlotResponse | undefined
504
+
505
+ // Destroy instance
506
+ destroy(): void
507
+
508
+ // Event listeners
509
+ on(event: string, handler: Function): void
510
+ off(event: string, handler: Function): void
511
+ ```
512
+
513
+ #### Events
514
+
515
+ - `adsLoading` - Started requesting ads
516
+ - `adsUpdated` - Ad data updated
517
+ - `adsError` - Request failed
518
+ - `adClicked` - Ad clicked
519
+ - `adImpression` - Ad impression
520
+
248
521
  ---
249
522
 
250
- **Version**: `0.1.0` | **License**: MIT
523
+ ## Version Info
524
+
525
+ - Current version: `0.1.12`
526
+ - License: MIT
527
+ - Repository: [GitHub](https://github.com/action-x/ad-sdk)
528
+
529
+ ---
530
+
531
+ ## Support
532
+
533
+ For questions, contact ActionX support or submit an Issue.