@ai-ad-network/frontend-sdk 1.1.1 → 1.1.2
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/dist/components/EnhancedContent.d.ts +50 -1
- package/dist/components/EntityLinkAd.simple.d.ts +76 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.esm.js +321 -40
- package/dist/index.js +321 -40
- package/dist/universal/index.d.ts +132 -0
- package/dist/version.d.ts +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { EntityInfo, EnhancedContentProps } from '@/types';
|
|
1
|
+
import type { EntityInfo, EnhancementOptions, EnhancedContentProps, KoahAd } from '@/types';
|
|
2
2
|
/**
|
|
3
3
|
* EnhancedContent Component
|
|
4
4
|
*
|
|
@@ -51,3 +51,52 @@ export declare function useEntityStats(entities: EntityInfo[]): {
|
|
|
51
51
|
byType: Record<string, number>;
|
|
52
52
|
avgConfidence: number;
|
|
53
53
|
};
|
|
54
|
+
/**
|
|
55
|
+
* EnhancedContentV2 Props
|
|
56
|
+
*/
|
|
57
|
+
export interface EnhancedContentV2Props {
|
|
58
|
+
/** The ad data containing entity_link_content */
|
|
59
|
+
ad: KoahAd;
|
|
60
|
+
/** AI response context for styling */
|
|
61
|
+
aiContext?: string;
|
|
62
|
+
/** Custom class name */
|
|
63
|
+
className?: string;
|
|
64
|
+
/** Whether tracking is loading (unused but for API compatibility) */
|
|
65
|
+
isLoading?: boolean;
|
|
66
|
+
requestId: string;
|
|
67
|
+
slotId: string;
|
|
68
|
+
position: number;
|
|
69
|
+
totalAds: number;
|
|
70
|
+
enhancements?: EnhancementOptions;
|
|
71
|
+
onEntityClick?: (entity: EntityInfo) => void;
|
|
72
|
+
onImpression?: () => void;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* EnhancedContentV2 - Entity Link Ad component with Analytics API tracking
|
|
76
|
+
*
|
|
77
|
+
* Renders content with entity-linked affiliate URLs and tracks clicks
|
|
78
|
+
* using the new Analytics API (POST /api/v1/ads/click).
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```tsx
|
|
82
|
+
* <EnhancedContentV2
|
|
83
|
+
* ad={ad}
|
|
84
|
+
* requestId="req-123"
|
|
85
|
+
* slotId="slot-entity-link"
|
|
86
|
+
* position={0}
|
|
87
|
+
* totalAds={3}
|
|
88
|
+
* />
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
91
|
+
export declare function EnhancedContentV2({ ad, aiContext, className, isLoading: _isLoading, requestId, slotId, position, totalAds, enhancements, onEntityClick, onImpression, }: EnhancedContentV2Props): import("react/jsx-runtime").JSX.Element;
|
|
92
|
+
/**
|
|
93
|
+
* Convenience components for V2
|
|
94
|
+
*/
|
|
95
|
+
/**
|
|
96
|
+
* Inline variant - for embedding within existing text
|
|
97
|
+
*/
|
|
98
|
+
export declare function EnhancedContentV2Inline(props: Omit<EnhancedContentV2Props, 'className'>): import("react/jsx-runtime").JSX.Element;
|
|
99
|
+
/**
|
|
100
|
+
* Block variant - for standalone content blocks
|
|
101
|
+
*/
|
|
102
|
+
export declare function EnhancedContentV2Block(props: EnhancedContentV2Props): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import type { KoahAd, BadgeStyle, OverlapStrategy } from '@/types';
|
|
2
|
+
import type { EntityInfo } from '@/types';
|
|
3
|
+
/**
|
|
4
|
+
* 🆕 Simplified EntityLinkAd Props
|
|
5
|
+
*
|
|
6
|
+
* This component automatically handles all analytics tracking parameters.
|
|
7
|
+
* You only need to provide the ad data and slot ID.
|
|
8
|
+
*/
|
|
9
|
+
export interface EntityLinkAdProps {
|
|
10
|
+
/** The ad data to display (must contain entity_link_content) */
|
|
11
|
+
ad: KoahAd;
|
|
12
|
+
/** Slot identifier (business concept, e.g., "inline-entity", "content-enhancement") */
|
|
13
|
+
slotId: string;
|
|
14
|
+
/** AI response context for template selection */
|
|
15
|
+
aiContext?: string;
|
|
16
|
+
/** Custom class name */
|
|
17
|
+
className?: string;
|
|
18
|
+
/** Loading state */
|
|
19
|
+
isLoading?: boolean;
|
|
20
|
+
/** Badge style for entity links */
|
|
21
|
+
badgeStyle?: BadgeStyle;
|
|
22
|
+
/** Strategy for handling overlapping entities */
|
|
23
|
+
overlapStrategy?: OverlapStrategy;
|
|
24
|
+
/** Maximum number of entity links to show */
|
|
25
|
+
maxLinks?: number;
|
|
26
|
+
/** Minimum confidence threshold for entities */
|
|
27
|
+
minConfidence?: number;
|
|
28
|
+
/** Custom click callback (in addition to automatic tracking) */
|
|
29
|
+
onEntityClick?: (entity: EntityInfo) => void;
|
|
30
|
+
/** Custom impression callback (in addition to automatic tracking) */
|
|
31
|
+
onImpression?: () => void;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* EntityLinkAd - Simplified component with automatic analytics tracking
|
|
35
|
+
*
|
|
36
|
+
* This component automatically injects all analytics tracking parameters
|
|
37
|
+
* (requestId, position, totalAds) from the SDK context and renders
|
|
38
|
+
* entity-linked content with click tracking.
|
|
39
|
+
*
|
|
40
|
+
* @example
|
|
41
|
+
* ```tsx
|
|
42
|
+
* // ✅ Simple usage - automatic tracking
|
|
43
|
+
* import { EntityLinkAd } from '@ai-ad-network/frontend-sdk';
|
|
44
|
+
*
|
|
45
|
+
* <EntityLinkAd
|
|
46
|
+
* ad={ad}
|
|
47
|
+
* slotId="inline-entity"
|
|
48
|
+
* aiContext={aiResponse}
|
|
49
|
+
* badgeStyle="subtle"
|
|
50
|
+
* />
|
|
51
|
+
*
|
|
52
|
+
* // With multiple ads
|
|
53
|
+
* {ads.map(ad => (
|
|
54
|
+
* <EntityLinkAd
|
|
55
|
+
* key={ad.id}
|
|
56
|
+
* ad={ad}
|
|
57
|
+
* slotId="inline-entity"
|
|
58
|
+
* />
|
|
59
|
+
* ))}
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @remarks
|
|
63
|
+
* For advanced use cases where you need explicit control over analytics parameters,
|
|
64
|
+
* use `EnhancedContentV2` directly.
|
|
65
|
+
*/
|
|
66
|
+
export declare function EntityLinkAd({ ad, slotId, aiContext, className, isLoading, badgeStyle, overlapStrategy, maxLinks, minConfidence, onEntityClick, onImpression, }: EntityLinkAdProps): import("react/jsx-runtime").JSX.Element;
|
|
67
|
+
/**
|
|
68
|
+
* EntityLinkAdInline - Inline variant for embedding within existing text
|
|
69
|
+
*/
|
|
70
|
+
export declare function EntityLinkAdInline(props: Omit<EntityLinkAdProps, 'className'>): import("react/jsx-runtime").JSX.Element;
|
|
71
|
+
/**
|
|
72
|
+
* EntityLinkAdBlock - Block variant for standalone content blocks
|
|
73
|
+
*/
|
|
74
|
+
export declare function EntityLinkAdBlock(props: EntityLinkAdProps): import("react/jsx-runtime").JSX.Element;
|
|
75
|
+
export { EnhancedContentV2 } from './EnhancedContent';
|
|
76
|
+
export type { EnhancedContentV2Props } from './EnhancedContent';
|
package/dist/index.d.ts
CHANGED
|
@@ -35,7 +35,11 @@ export { ActionCardAd } from './components/ActionCardAd.simple';
|
|
|
35
35
|
export { ActionCardAdV2 } from './components/ActionCardAd';
|
|
36
36
|
export type { ActionCardAdV2Props } from './components/ActionCardAd';
|
|
37
37
|
export type { ActionCardAdProps } from './components/ActionCardAd.simple';
|
|
38
|
+
export { EntityLinkAd, EntityLinkAdInline, EntityLinkAdBlock } from './components/EntityLinkAd.simple';
|
|
39
|
+
export { EnhancedContentV2, EnhancedContentV2Inline, EnhancedContentV2Block, } from './components/EnhancedContent';
|
|
38
40
|
export { EnhancedContent, EnhancedContentInline, EnhancedContentBlock, useEntityStats, } from './components/EnhancedContent';
|
|
41
|
+
export type { EnhancedContentV2Props } from './components/EnhancedContent';
|
|
42
|
+
export type { EntityLinkAdProps } from './components/EntityLinkAd.simple';
|
|
39
43
|
export { SuffixAd } from './components/SuffixAd.simple';
|
|
40
44
|
export { SuffixAdV2 } from './components/SuffixAd';
|
|
41
45
|
export { SuffixAdInline, SuffixAdMinimal, } from './components/SuffixAd';
|
package/dist/index.esm.js
CHANGED
|
@@ -3,7 +3,7 @@ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { en
|
|
|
3
3
|
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
4
4
|
import React, { useState, useRef, useCallback, useEffect, createContext, useContext, useMemo, Component } from "react";
|
|
5
5
|
import useSWR from "swr";
|
|
6
|
-
const SDK_VERSION = "1.1.
|
|
6
|
+
const SDK_VERSION = "1.1.2";
|
|
7
7
|
var IntentType = /* @__PURE__ */ ((IntentType2) => {
|
|
8
8
|
IntentType2["SHOPPING"] = "shopping";
|
|
9
9
|
IntentType2["LEAD_GEN"] = "lead_gen";
|
|
@@ -1381,6 +1381,19 @@ function createSession(config = {}) {
|
|
|
1381
1381
|
clearSessionId: () => manager.clearSessionId()
|
|
1382
1382
|
};
|
|
1383
1383
|
}
|
|
1384
|
+
const index = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
1385
|
+
__proto__: null,
|
|
1386
|
+
AnalyticsSender,
|
|
1387
|
+
SessionManager,
|
|
1388
|
+
createAnalytics,
|
|
1389
|
+
createSession,
|
|
1390
|
+
generateViewToken,
|
|
1391
|
+
getSessionId,
|
|
1392
|
+
trackAdClick,
|
|
1393
|
+
trackAdImpression,
|
|
1394
|
+
trackClicksBatch,
|
|
1395
|
+
trackImpressionsBatch
|
|
1396
|
+
}, Symbol.toStringTag, { value: "Module" }));
|
|
1384
1397
|
function useAdTracking(ad, options = {}) {
|
|
1385
1398
|
const {
|
|
1386
1399
|
intersectionThreshold = 0.5,
|
|
@@ -2996,16 +3009,16 @@ const createParseClassName = (config) => {
|
|
|
2996
3009
|
let bracketDepth = 0;
|
|
2997
3010
|
let modifierStart = 0;
|
|
2998
3011
|
let postfixModifierPosition;
|
|
2999
|
-
for (let
|
|
3000
|
-
let currentCharacter = className[
|
|
3012
|
+
for (let index2 = 0; index2 < className.length; index2++) {
|
|
3013
|
+
let currentCharacter = className[index2];
|
|
3001
3014
|
if (bracketDepth === 0) {
|
|
3002
|
-
if (currentCharacter === firstSeparatorCharacter && (isSeparatorSingleCharacter || className.slice(
|
|
3003
|
-
modifiers.push(className.slice(modifierStart,
|
|
3004
|
-
modifierStart =
|
|
3015
|
+
if (currentCharacter === firstSeparatorCharacter && (isSeparatorSingleCharacter || className.slice(index2, index2 + separatorLength) === separator)) {
|
|
3016
|
+
modifiers.push(className.slice(modifierStart, index2));
|
|
3017
|
+
modifierStart = index2 + separatorLength;
|
|
3005
3018
|
continue;
|
|
3006
3019
|
}
|
|
3007
3020
|
if (currentCharacter === "/") {
|
|
3008
|
-
postfixModifierPosition =
|
|
3021
|
+
postfixModifierPosition = index2;
|
|
3009
3022
|
continue;
|
|
3010
3023
|
}
|
|
3011
3024
|
}
|
|
@@ -3067,8 +3080,8 @@ const mergeClassList = (classList, configUtils) => {
|
|
|
3067
3080
|
const classGroupsInConflict = [];
|
|
3068
3081
|
const classNames = classList.trim().split(SPLIT_CLASSES_REGEX);
|
|
3069
3082
|
let result = "";
|
|
3070
|
-
for (let
|
|
3071
|
-
const originalClassName = classNames[
|
|
3083
|
+
for (let index2 = classNames.length - 1; index2 >= 0; index2 -= 1) {
|
|
3084
|
+
const originalClassName = classNames[index2];
|
|
3072
3085
|
const {
|
|
3073
3086
|
modifiers,
|
|
3074
3087
|
hasImportantModifier,
|
|
@@ -3106,12 +3119,12 @@ const mergeClassList = (classList, configUtils) => {
|
|
|
3106
3119
|
return result;
|
|
3107
3120
|
};
|
|
3108
3121
|
function twJoin() {
|
|
3109
|
-
let
|
|
3122
|
+
let index2 = 0;
|
|
3110
3123
|
let argument;
|
|
3111
3124
|
let resolvedValue;
|
|
3112
3125
|
let string = "";
|
|
3113
|
-
while (
|
|
3114
|
-
if (argument = arguments[
|
|
3126
|
+
while (index2 < arguments.length) {
|
|
3127
|
+
if (argument = arguments[index2++]) {
|
|
3115
3128
|
if (resolvedValue = toValue(argument)) {
|
|
3116
3129
|
string && (string += " ");
|
|
3117
3130
|
string += resolvedValue;
|
|
@@ -5594,7 +5607,7 @@ function ActionCardAdV2({
|
|
|
5594
5607
|
}
|
|
5595
5608
|
);
|
|
5596
5609
|
}
|
|
5597
|
-
function getAnalyticsFromGlobal$
|
|
5610
|
+
function getAnalyticsFromGlobal$3(adId, slotId) {
|
|
5598
5611
|
if (typeof window === "undefined") return null;
|
|
5599
5612
|
const analytics = window.__AD_ANALYTICS__;
|
|
5600
5613
|
if (!analytics || !analytics.requestId) return null;
|
|
@@ -5627,7 +5640,7 @@ function ActionCardAd({
|
|
|
5627
5640
|
onImpression
|
|
5628
5641
|
}) {
|
|
5629
5642
|
const analytics = useMemo(() => {
|
|
5630
|
-
return getAnalyticsFromGlobal$
|
|
5643
|
+
return getAnalyticsFromGlobal$3(ad.id, slotId);
|
|
5631
5644
|
}, [ad.id, slotId]);
|
|
5632
5645
|
useEffect(() => {
|
|
5633
5646
|
if (analytics) {
|
|
@@ -5732,9 +5745,9 @@ function EnhancedContent({
|
|
|
5732
5745
|
onEntityClick == null ? void 0 : onEntityClick(entity);
|
|
5733
5746
|
window.open(url, "_blank", "noopener,noreferrer");
|
|
5734
5747
|
}, [onEntityClick]);
|
|
5735
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("enhanced-content", className), children: processedContent.map((part,
|
|
5748
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("enhanced-content", className), children: processedContent.map((part, index2) => {
|
|
5736
5749
|
if (part.type === "text") {
|
|
5737
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: part.content },
|
|
5750
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: part.content }, index2);
|
|
5738
5751
|
}
|
|
5739
5752
|
const { entity, url } = part;
|
|
5740
5753
|
const isHovered = hoveredEntity === entity.text;
|
|
@@ -5768,7 +5781,7 @@ function EnhancedContent({
|
|
|
5768
5781
|
renderBadge(entity, badgeStyle, isHovered)
|
|
5769
5782
|
]
|
|
5770
5783
|
},
|
|
5771
|
-
|
|
5784
|
+
index2
|
|
5772
5785
|
);
|
|
5773
5786
|
}) });
|
|
5774
5787
|
}
|
|
@@ -5786,17 +5799,17 @@ function buildEnhancedContent(content, entities, entityUrlMap, _badgeStyle) {
|
|
|
5786
5799
|
for (const entity of sortedEntities) {
|
|
5787
5800
|
const replacement = entityUrlMap.get(entity.text);
|
|
5788
5801
|
if (!replacement) continue;
|
|
5789
|
-
let
|
|
5802
|
+
let index2;
|
|
5790
5803
|
if (entity.startPosition >= 0) {
|
|
5791
|
-
|
|
5804
|
+
index2 = entity.startPosition;
|
|
5792
5805
|
} else {
|
|
5793
|
-
|
|
5806
|
+
index2 = content.toLowerCase().indexOf(entity.text.toLowerCase(), lastIndex);
|
|
5794
5807
|
}
|
|
5795
|
-
if (
|
|
5796
|
-
if (
|
|
5808
|
+
if (index2 === -1) continue;
|
|
5809
|
+
if (index2 > lastIndex) {
|
|
5797
5810
|
parts.push({
|
|
5798
5811
|
type: "text",
|
|
5799
|
-
content: content.substring(lastIndex,
|
|
5812
|
+
content: content.substring(lastIndex, index2)
|
|
5800
5813
|
});
|
|
5801
5814
|
}
|
|
5802
5815
|
parts.push({
|
|
@@ -5805,7 +5818,7 @@ function buildEnhancedContent(content, entities, entityUrlMap, _badgeStyle) {
|
|
|
5805
5818
|
entity,
|
|
5806
5819
|
url: replacement.affiliateUrl
|
|
5807
5820
|
});
|
|
5808
|
-
lastIndex =
|
|
5821
|
+
lastIndex = index2 + entity.text.length;
|
|
5809
5822
|
}
|
|
5810
5823
|
if (lastIndex < content.length) {
|
|
5811
5824
|
parts.push({
|
|
@@ -5826,10 +5839,10 @@ function filterOverlappingEntities(entities, content, strategy) {
|
|
|
5826
5839
|
start = entity.startPosition;
|
|
5827
5840
|
end = entity.endPosition;
|
|
5828
5841
|
} else {
|
|
5829
|
-
const
|
|
5830
|
-
if (
|
|
5831
|
-
start =
|
|
5832
|
-
end =
|
|
5842
|
+
const index2 = content.toLowerCase().indexOf(entity.text.toLowerCase());
|
|
5843
|
+
if (index2 === -1) continue;
|
|
5844
|
+
start = index2;
|
|
5845
|
+
end = index2 + entity.text.length;
|
|
5833
5846
|
}
|
|
5834
5847
|
const overlaps = usedRanges.some(
|
|
5835
5848
|
(range) => !(end <= range.start || start >= range.end)
|
|
@@ -5942,6 +5955,268 @@ function useEntityStats(entities) {
|
|
|
5942
5955
|
};
|
|
5943
5956
|
}, [entities]);
|
|
5944
5957
|
}
|
|
5958
|
+
function EnhancedContentV2({
|
|
5959
|
+
ad,
|
|
5960
|
+
aiContext,
|
|
5961
|
+
className,
|
|
5962
|
+
isLoading: _isLoading,
|
|
5963
|
+
requestId,
|
|
5964
|
+
slotId,
|
|
5965
|
+
position,
|
|
5966
|
+
totalAds,
|
|
5967
|
+
enhancements,
|
|
5968
|
+
onEntityClick,
|
|
5969
|
+
onImpression
|
|
5970
|
+
}) {
|
|
5971
|
+
var _a;
|
|
5972
|
+
const { impressionRef } = useAdTrackingV2({
|
|
5973
|
+
ad,
|
|
5974
|
+
requestId,
|
|
5975
|
+
slotId,
|
|
5976
|
+
position,
|
|
5977
|
+
totalAds,
|
|
5978
|
+
onImpression
|
|
5979
|
+
});
|
|
5980
|
+
const [hoveredEntity, setHoveredEntity] = useState(null);
|
|
5981
|
+
const entityContent = (_a = ad.content) == null ? void 0 : _a.entity_link_content;
|
|
5982
|
+
const options = {
|
|
5983
|
+
...DEFAULT_OPTIONS,
|
|
5984
|
+
...enhancements,
|
|
5985
|
+
...entityContent ? {
|
|
5986
|
+
entities: entityContent.entities,
|
|
5987
|
+
replacements: entityContent.replacements
|
|
5988
|
+
} : {}
|
|
5989
|
+
};
|
|
5990
|
+
const content = (entityContent == null ? void 0 : entityContent.originalText) || aiContext || "";
|
|
5991
|
+
const processedContent = useMemo(() => {
|
|
5992
|
+
const { entities, replacements, maxLinks, minConfidence, badgeStyle, overlapStrategy } = options;
|
|
5993
|
+
const entityUrlMap = /* @__PURE__ */ new Map();
|
|
5994
|
+
for (const replacement of replacements) {
|
|
5995
|
+
entityUrlMap.set(replacement.originalText, replacement);
|
|
5996
|
+
}
|
|
5997
|
+
const minConf = minConfidence ?? DEFAULT_OPTIONS.minConfidence;
|
|
5998
|
+
const validEntities = entities.filter((e) => {
|
|
5999
|
+
const hasUrl = entityUrlMap.has(e.text);
|
|
6000
|
+
const confidenceOk = e.confidence >= minConf;
|
|
6001
|
+
return hasUrl && confidenceOk;
|
|
6002
|
+
});
|
|
6003
|
+
const sortedEntities = [...validEntities].sort((a, b) => {
|
|
6004
|
+
if (a.startPosition >= 0 && b.startPosition >= 0) {
|
|
6005
|
+
return a.startPosition - b.startPosition;
|
|
6006
|
+
}
|
|
6007
|
+
const aPos = content.toLowerCase().indexOf(a.text.toLowerCase());
|
|
6008
|
+
const bPos = content.toLowerCase().indexOf(b.text.toLowerCase());
|
|
6009
|
+
if (aPos === -1 && bPos === -1) {
|
|
6010
|
+
return b.confidence - a.confidence;
|
|
6011
|
+
}
|
|
6012
|
+
if (aPos === -1) return 1;
|
|
6013
|
+
if (bPos === -1) return -1;
|
|
6014
|
+
return aPos - bPos;
|
|
6015
|
+
});
|
|
6016
|
+
const max = maxLinks ?? DEFAULT_OPTIONS.maxLinks;
|
|
6017
|
+
const limitedEntities = sortedEntities.slice(0, max);
|
|
6018
|
+
const strategy = overlapStrategy ?? DEFAULT_OPTIONS.overlapStrategy;
|
|
6019
|
+
const filteredEntities = filterOverlappingEntities(
|
|
6020
|
+
limitedEntities,
|
|
6021
|
+
content,
|
|
6022
|
+
strategy
|
|
6023
|
+
);
|
|
6024
|
+
const style = badgeStyle ?? DEFAULT_OPTIONS.badgeStyle;
|
|
6025
|
+
return buildEnhancedContent(content, filteredEntities, entityUrlMap, style);
|
|
6026
|
+
}, [content, options]);
|
|
6027
|
+
const handleEntityClick = useCallback(async (entity, url) => {
|
|
6028
|
+
var _a2;
|
|
6029
|
+
const { trackAdClick: trackAdClick2 } = await Promise.resolve().then(() => index);
|
|
6030
|
+
await trackAdClick2({
|
|
6031
|
+
requestId,
|
|
6032
|
+
adId: ad.id,
|
|
6033
|
+
destinationUrl: url,
|
|
6034
|
+
sessionId: "",
|
|
6035
|
+
// Will be auto-generated
|
|
6036
|
+
slotId,
|
|
6037
|
+
adTitle: entity.text,
|
|
6038
|
+
format: "entity_link",
|
|
6039
|
+
source: (_a2 = ad.content) == null ? void 0 : _a2.brand
|
|
6040
|
+
});
|
|
6041
|
+
onEntityClick == null ? void 0 : onEntityClick(entity);
|
|
6042
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
6043
|
+
}, [requestId, ad.id, ad.content, slotId, onEntityClick]);
|
|
6044
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6045
|
+
"div",
|
|
6046
|
+
{
|
|
6047
|
+
ref: impressionRef,
|
|
6048
|
+
className: cn("ad-component enhanced-content", className),
|
|
6049
|
+
children: processedContent.map((part, index2) => {
|
|
6050
|
+
if (part.type === "text") {
|
|
6051
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("span", { children: part.content }, index2);
|
|
6052
|
+
}
|
|
6053
|
+
const { entity, url } = part;
|
|
6054
|
+
const isHovered = hoveredEntity === entity.text;
|
|
6055
|
+
const badgeStyle = options.badgeStyle ?? DEFAULT_OPTIONS.badgeStyle;
|
|
6056
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(
|
|
6057
|
+
"a",
|
|
6058
|
+
{
|
|
6059
|
+
href: url,
|
|
6060
|
+
onClick: (e) => {
|
|
6061
|
+
e.preventDefault();
|
|
6062
|
+
handleEntityClick(entity, url);
|
|
6063
|
+
},
|
|
6064
|
+
onMouseEnter: () => setHoveredEntity(entity.text),
|
|
6065
|
+
onMouseLeave: () => setHoveredEntity(null),
|
|
6066
|
+
className: cn(
|
|
6067
|
+
"entity-link inline-flex items-center gap-1 transition-all duration-200",
|
|
6068
|
+
"text-ad-primary hover:text-ad-primary/80 hover:underline",
|
|
6069
|
+
"font-medium cursor-pointer",
|
|
6070
|
+
"focus:outline-none focus:ring-2 focus:ring-ad-primary/50 focus:ring-offset-2 rounded",
|
|
6071
|
+
{
|
|
6072
|
+
"opacity-90": isHovered
|
|
6073
|
+
}
|
|
6074
|
+
),
|
|
6075
|
+
"data-entity-type": entity.type,
|
|
6076
|
+
"data-entity-text": entity.text,
|
|
6077
|
+
"data-tracking": `entity_${entity.text.toLowerCase().replace(/\s+/g, "_")}`,
|
|
6078
|
+
rel: "sponsored noopener noreferrer",
|
|
6079
|
+
target: "_blank",
|
|
6080
|
+
children: [
|
|
6081
|
+
part.content,
|
|
6082
|
+
renderBadge(entity, badgeStyle, isHovered)
|
|
6083
|
+
]
|
|
6084
|
+
},
|
|
6085
|
+
index2
|
|
6086
|
+
);
|
|
6087
|
+
})
|
|
6088
|
+
}
|
|
6089
|
+
);
|
|
6090
|
+
}
|
|
6091
|
+
function EnhancedContentV2Inline(props) {
|
|
6092
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6093
|
+
EnhancedContentV2,
|
|
6094
|
+
{
|
|
6095
|
+
...props,
|
|
6096
|
+
className: "inline"
|
|
6097
|
+
}
|
|
6098
|
+
);
|
|
6099
|
+
}
|
|
6100
|
+
function EnhancedContentV2Block(props) {
|
|
6101
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6102
|
+
EnhancedContentV2,
|
|
6103
|
+
{
|
|
6104
|
+
...props,
|
|
6105
|
+
className: "block"
|
|
6106
|
+
}
|
|
6107
|
+
);
|
|
6108
|
+
}
|
|
6109
|
+
function getAnalyticsFromGlobal$2(adId, slotId) {
|
|
6110
|
+
if (typeof window === "undefined") return null;
|
|
6111
|
+
const analytics = window.__AD_ANALYTICS__;
|
|
6112
|
+
if (!analytics || !analytics.requestId) return null;
|
|
6113
|
+
const { requestId, slotsMap, getAdAnalytics } = analytics;
|
|
6114
|
+
if (getAdAnalytics) {
|
|
6115
|
+
return getAdAnalytics(adId, slotId);
|
|
6116
|
+
}
|
|
6117
|
+
for (const [sid, slotInfo] of Object.entries(slotsMap || {})) {
|
|
6118
|
+
if (slotId && sid !== slotId) continue;
|
|
6119
|
+
const info = slotInfo;
|
|
6120
|
+
const position = (info.ads || []).findIndex((a) => a.id === adId);
|
|
6121
|
+
if (position !== -1) {
|
|
6122
|
+
return {
|
|
6123
|
+
requestId,
|
|
6124
|
+
slotId: sid,
|
|
6125
|
+
position,
|
|
6126
|
+
totalAds: info.count
|
|
6127
|
+
};
|
|
6128
|
+
}
|
|
6129
|
+
}
|
|
6130
|
+
return null;
|
|
6131
|
+
}
|
|
6132
|
+
function EntityLinkAd({
|
|
6133
|
+
ad,
|
|
6134
|
+
slotId,
|
|
6135
|
+
aiContext,
|
|
6136
|
+
className,
|
|
6137
|
+
isLoading = false,
|
|
6138
|
+
badgeStyle,
|
|
6139
|
+
overlapStrategy,
|
|
6140
|
+
maxLinks,
|
|
6141
|
+
minConfidence,
|
|
6142
|
+
onEntityClick,
|
|
6143
|
+
onImpression
|
|
6144
|
+
}) {
|
|
6145
|
+
const analytics = useMemo(() => {
|
|
6146
|
+
return getAnalyticsFromGlobal$2(ad.id, slotId);
|
|
6147
|
+
}, [ad.id, slotId]);
|
|
6148
|
+
useEffect(() => {
|
|
6149
|
+
if (analytics) {
|
|
6150
|
+
console.log(
|
|
6151
|
+
`[EntityLinkAd] Auto-tracking enabled: requestId=${analytics.requestId}, slotId=${analytics.slotId}, position=${analytics.position}, totalAds=${analytics.totalAds}`
|
|
6152
|
+
);
|
|
6153
|
+
} else {
|
|
6154
|
+
console.warn(
|
|
6155
|
+
`[EntityLinkAd] Analytics data not found. Make sure useAiAds has completed. Falling back to basic rendering without tracking.`
|
|
6156
|
+
);
|
|
6157
|
+
}
|
|
6158
|
+
}, [analytics]);
|
|
6159
|
+
const enhancementOptions = useMemo(() => {
|
|
6160
|
+
const options = {};
|
|
6161
|
+
if (badgeStyle !== void 0) options.badgeStyle = badgeStyle;
|
|
6162
|
+
if (overlapStrategy !== void 0) options.overlapStrategy = overlapStrategy;
|
|
6163
|
+
if (maxLinks !== void 0) options.maxLinks = maxLinks;
|
|
6164
|
+
if (minConfidence !== void 0) options.minConfidence = minConfidence;
|
|
6165
|
+
return Object.keys(options).length > 0 ? options : void 0;
|
|
6166
|
+
}, [badgeStyle, overlapStrategy, maxLinks, minConfidence]);
|
|
6167
|
+
if (!analytics || !analytics.requestId) {
|
|
6168
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6169
|
+
EnhancedContentV2,
|
|
6170
|
+
{
|
|
6171
|
+
ad,
|
|
6172
|
+
aiContext,
|
|
6173
|
+
requestId: "",
|
|
6174
|
+
slotId,
|
|
6175
|
+
position: 0,
|
|
6176
|
+
totalAds: 1,
|
|
6177
|
+
className,
|
|
6178
|
+
isLoading,
|
|
6179
|
+
enhancements: enhancementOptions,
|
|
6180
|
+
onEntityClick,
|
|
6181
|
+
onImpression
|
|
6182
|
+
}
|
|
6183
|
+
);
|
|
6184
|
+
}
|
|
6185
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6186
|
+
EnhancedContentV2,
|
|
6187
|
+
{
|
|
6188
|
+
ad,
|
|
6189
|
+
aiContext,
|
|
6190
|
+
requestId: analytics.requestId,
|
|
6191
|
+
slotId: analytics.slotId,
|
|
6192
|
+
position: analytics.position,
|
|
6193
|
+
totalAds: analytics.totalAds,
|
|
6194
|
+
className,
|
|
6195
|
+
isLoading,
|
|
6196
|
+
enhancements: enhancementOptions,
|
|
6197
|
+
onEntityClick,
|
|
6198
|
+
onImpression
|
|
6199
|
+
}
|
|
6200
|
+
);
|
|
6201
|
+
}
|
|
6202
|
+
function EntityLinkAdInline(props) {
|
|
6203
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6204
|
+
EntityLinkAd,
|
|
6205
|
+
{
|
|
6206
|
+
...props,
|
|
6207
|
+
className: "inline"
|
|
6208
|
+
}
|
|
6209
|
+
);
|
|
6210
|
+
}
|
|
6211
|
+
function EntityLinkAdBlock(props) {
|
|
6212
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6213
|
+
EntityLinkAd,
|
|
6214
|
+
{
|
|
6215
|
+
...props,
|
|
6216
|
+
className: "block"
|
|
6217
|
+
}
|
|
6218
|
+
);
|
|
6219
|
+
}
|
|
5945
6220
|
class TextWeaver {
|
|
5946
6221
|
constructor() {
|
|
5947
6222
|
__publicField(this, "suffixTemplates", [
|
|
@@ -6451,8 +6726,8 @@ function FollowUpAdMixed({
|
|
|
6451
6726
|
if (!ad) return questions.map((q) => ({ type: "question", content: q }));
|
|
6452
6727
|
const result = [];
|
|
6453
6728
|
let adInserted = false;
|
|
6454
|
-
questions.forEach((question,
|
|
6455
|
-
if (
|
|
6729
|
+
questions.forEach((question, index2) => {
|
|
6730
|
+
if (index2 === adPosition && !adInserted) {
|
|
6456
6731
|
result.push({ type: "ad", content: ad });
|
|
6457
6732
|
adInserted = true;
|
|
6458
6733
|
}
|
|
@@ -6463,7 +6738,7 @@ function FollowUpAdMixed({
|
|
|
6463
6738
|
}
|
|
6464
6739
|
return result;
|
|
6465
6740
|
}, [questions, ad, adPosition]);
|
|
6466
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("flex flex-wrap gap-2", className), children: items.map((item,
|
|
6741
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("flex flex-wrap gap-2", className), children: items.map((item, index2) => {
|
|
6467
6742
|
if (item.type === "ad") {
|
|
6468
6743
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6469
6744
|
FollowUpAd,
|
|
@@ -6473,7 +6748,7 @@ function FollowUpAdMixed({
|
|
|
6473
6748
|
onClick: onAdClick,
|
|
6474
6749
|
onImpression: onAdImpression
|
|
6475
6750
|
},
|
|
6476
|
-
`ad-${
|
|
6751
|
+
`ad-${index2}`
|
|
6477
6752
|
);
|
|
6478
6753
|
}
|
|
6479
6754
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
@@ -6489,7 +6764,7 @@ function FollowUpAdMixed({
|
|
|
6489
6764
|
),
|
|
6490
6765
|
children: String(item.content)
|
|
6491
6766
|
},
|
|
6492
|
-
`question-${
|
|
6767
|
+
`question-${index2}`
|
|
6493
6768
|
);
|
|
6494
6769
|
}) });
|
|
6495
6770
|
}
|
|
@@ -6636,8 +6911,8 @@ function SourceListWithAds({
|
|
|
6636
6911
|
if (!ad) return sources.map((s) => ({ type: "source", content: s }));
|
|
6637
6912
|
const result = [];
|
|
6638
6913
|
let adInserted = false;
|
|
6639
|
-
sources.forEach((source,
|
|
6640
|
-
if (
|
|
6914
|
+
sources.forEach((source, index2) => {
|
|
6915
|
+
if (index2 === adPosition && !adInserted) {
|
|
6641
6916
|
result.push({ type: "ad", content: ad });
|
|
6642
6917
|
adInserted = true;
|
|
6643
6918
|
}
|
|
@@ -6648,7 +6923,7 @@ function SourceListWithAds({
|
|
|
6648
6923
|
}
|
|
6649
6924
|
return result;
|
|
6650
6925
|
}, [sources, ad, adPosition]);
|
|
6651
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("space-y-1", className), children: items.map((item,
|
|
6926
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: cn("space-y-1", className), children: items.map((item, index2) => {
|
|
6652
6927
|
if (item.type === "ad") {
|
|
6653
6928
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(
|
|
6654
6929
|
SponsoredSource,
|
|
@@ -6658,7 +6933,7 @@ function SourceListWithAds({
|
|
|
6658
6933
|
onClick: onAdClick,
|
|
6659
6934
|
onImpression: onAdImpression
|
|
6660
6935
|
},
|
|
6661
|
-
`ad-${
|
|
6936
|
+
`ad-${index2}`
|
|
6662
6937
|
);
|
|
6663
6938
|
}
|
|
6664
6939
|
const source = item.content;
|
|
@@ -6682,7 +6957,7 @@ function SourceListWithAds({
|
|
|
6682
6957
|
)
|
|
6683
6958
|
]
|
|
6684
6959
|
},
|
|
6685
|
-
`source-${
|
|
6960
|
+
`source-${index2}`
|
|
6686
6961
|
);
|
|
6687
6962
|
}) });
|
|
6688
6963
|
}
|
|
@@ -7529,7 +7804,7 @@ function StaticAdContainer({
|
|
|
7529
7804
|
className
|
|
7530
7805
|
),
|
|
7531
7806
|
style: { gap: `${gap}px` },
|
|
7532
|
-
children: visibleAds.map((ad,
|
|
7807
|
+
children: visibleAds.map((ad, index2) => /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "animate-slide-up", style: { animationDelay: `${index2 * 30}ms` }, children: /* @__PURE__ */ jsxRuntimeExports.jsx(StaticAd, { ad, size }) }, ad.id))
|
|
7533
7808
|
}
|
|
7534
7809
|
);
|
|
7535
7810
|
}
|
|
@@ -7794,7 +8069,7 @@ function AdAnalyticsDashboard({
|
|
|
7794
8069
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6", children: [
|
|
7795
8070
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white/70 backdrop-blur-md rounded-2xl border border-ad-border/50 shadow-glass p-6", children: [
|
|
7796
8071
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-ad-text-primary mb-4", children: "Top Performing Ads" }),
|
|
7797
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-4", children: metrics.sort((a, b) => b.ctr - a.ctr).slice(0, 5).map((ad,
|
|
8072
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "space-y-4", children: metrics.sort((a, b) => b.ctr - a.ctr).slice(0, 5).map((ad, index2) => /* @__PURE__ */ jsxRuntimeExports.jsx(AdPerformanceRow, { ad, rank: index2 + 1 }, ad.adId)) })
|
|
7798
8073
|
] }),
|
|
7799
8074
|
showBySource && /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "bg-white/70 backdrop-blur-md rounded-2xl border border-ad-border/50 shadow-glass p-6", children: [
|
|
7800
8075
|
/* @__PURE__ */ jsxRuntimeExports.jsx("h3", { className: "text-lg font-bold text-ad-text-primary mb-4", children: "By Ad Source" }),
|
|
@@ -8805,6 +9080,12 @@ export {
|
|
|
8805
9080
|
EnhancedContent,
|
|
8806
9081
|
EnhancedContentBlock,
|
|
8807
9082
|
EnhancedContentInline,
|
|
9083
|
+
EnhancedContentV2,
|
|
9084
|
+
EnhancedContentV2Block,
|
|
9085
|
+
EnhancedContentV2Inline,
|
|
9086
|
+
EntityLinkAd,
|
|
9087
|
+
EntityLinkAdBlock,
|
|
9088
|
+
EntityLinkAdInline,
|
|
8808
9089
|
FollowUpAd,
|
|
8809
9090
|
FollowUpAdMixed,
|
|
8810
9091
|
FullPageLoading,
|