@adland/embed 0.1.1
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/CHANGELOG.md +47 -0
- package/README.md +72 -0
- package/dist/adland-farcaster.js +285 -0
- package/dist/adland-farcaster.js.map +1 -0
- package/dist/adland.js +147 -0
- package/dist/adland.js.map +1 -0
- package/dist/index.cjs +337 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +329 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@adland/react/core');
|
|
4
|
+
|
|
5
|
+
// src/index.ts
|
|
6
|
+
var AD_PARTS = [
|
|
7
|
+
"card",
|
|
8
|
+
"media",
|
|
9
|
+
"body",
|
|
10
|
+
"title",
|
|
11
|
+
"description",
|
|
12
|
+
"badge",
|
|
13
|
+
"label"
|
|
14
|
+
];
|
|
15
|
+
var TYPE_LABELS = {
|
|
16
|
+
link: "Link",
|
|
17
|
+
cast: "Cast",
|
|
18
|
+
miniapp: "Mini App",
|
|
19
|
+
token: "Token",
|
|
20
|
+
farcasterProfile: "Profile"
|
|
21
|
+
};
|
|
22
|
+
function escapeHtml(value) {
|
|
23
|
+
return value.replace(
|
|
24
|
+
/[&<>"']/g,
|
|
25
|
+
(c) => ({
|
|
26
|
+
"&": "&",
|
|
27
|
+
"<": "<",
|
|
28
|
+
">": ">",
|
|
29
|
+
'"': """,
|
|
30
|
+
"'": "'"
|
|
31
|
+
})[c]
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
function escapeAttr(value) {
|
|
35
|
+
return value.replace(/"/g, """).replace(/</g, "<");
|
|
36
|
+
}
|
|
37
|
+
var BASE_STYLES = `
|
|
38
|
+
:host { display: block; }
|
|
39
|
+
* { box-sizing: border-box; }
|
|
40
|
+
.adland-card {
|
|
41
|
+
position: relative;
|
|
42
|
+
display: flex;
|
|
43
|
+
gap: var(--adland-gap, 12px);
|
|
44
|
+
align-items: center;
|
|
45
|
+
padding: var(--adland-padding, 12px);
|
|
46
|
+
border: var(--adland-border-width, 1px) solid var(--adland-border, #e5e7eb);
|
|
47
|
+
border-radius: var(--adland-radius, 12px);
|
|
48
|
+
background: var(--adland-bg, #ffffff);
|
|
49
|
+
color: var(--adland-fg, #111827);
|
|
50
|
+
font-family: var(--adland-font, system-ui, -apple-system, sans-serif);
|
|
51
|
+
cursor: pointer;
|
|
52
|
+
text-decoration: none;
|
|
53
|
+
transition: border-color 0.15s ease, box-shadow 0.15s ease;
|
|
54
|
+
}
|
|
55
|
+
.adland-card:hover {
|
|
56
|
+
border-color: var(--adland-border-hover, #d1d5db);
|
|
57
|
+
box-shadow: var(--adland-shadow-hover, 0 1px 3px rgba(0, 0, 0, 0.08));
|
|
58
|
+
}
|
|
59
|
+
.adland-media {
|
|
60
|
+
width: var(--adland-media-size, 44px);
|
|
61
|
+
height: var(--adland-media-size, 44px);
|
|
62
|
+
border-radius: var(--adland-media-radius, 8px);
|
|
63
|
+
object-fit: cover;
|
|
64
|
+
flex: none;
|
|
65
|
+
background: var(--adland-media-bg, #f3f4f6);
|
|
66
|
+
}
|
|
67
|
+
.adland-body { min-width: 0; flex: 1; }
|
|
68
|
+
.adland-title {
|
|
69
|
+
margin: 0;
|
|
70
|
+
font-size: var(--adland-title-size, 14px);
|
|
71
|
+
font-weight: var(--adland-title-weight, 600);
|
|
72
|
+
line-height: 1.3;
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
text-overflow: ellipsis;
|
|
75
|
+
white-space: nowrap;
|
|
76
|
+
}
|
|
77
|
+
.adland-description {
|
|
78
|
+
margin: 2px 0 0;
|
|
79
|
+
font-size: var(--adland-description-size, 12px);
|
|
80
|
+
color: var(--adland-muted, #6b7280);
|
|
81
|
+
line-height: 1.4;
|
|
82
|
+
overflow: hidden;
|
|
83
|
+
text-overflow: ellipsis;
|
|
84
|
+
white-space: nowrap;
|
|
85
|
+
}
|
|
86
|
+
.adland-badge {
|
|
87
|
+
flex: none;
|
|
88
|
+
font-size: 10px;
|
|
89
|
+
font-weight: 600;
|
|
90
|
+
padding: 2px 6px;
|
|
91
|
+
border-radius: var(--adland-badge-radius, 6px);
|
|
92
|
+
background: var(--adland-accent-bg, #eef2ff);
|
|
93
|
+
color: var(--adland-accent-fg, #4338ca);
|
|
94
|
+
}
|
|
95
|
+
.adland-label {
|
|
96
|
+
position: absolute;
|
|
97
|
+
top: 6px;
|
|
98
|
+
right: 8px;
|
|
99
|
+
font-size: 9px;
|
|
100
|
+
letter-spacing: 0.04em;
|
|
101
|
+
text-transform: uppercase;
|
|
102
|
+
color: var(--adland-label, #9ca3af);
|
|
103
|
+
}
|
|
104
|
+
.adland-empty { justify-content: center; color: var(--adland-muted, #6b7280); }
|
|
105
|
+
`;
|
|
106
|
+
function cardMarkup(ad) {
|
|
107
|
+
const image = core.getAdImage(ad);
|
|
108
|
+
const title = core.getAdTitle(ad);
|
|
109
|
+
const description = core.getAdDescription(ad);
|
|
110
|
+
const type = core.getAdType(ad);
|
|
111
|
+
const badge = type ? TYPE_LABELS[type] ?? type : "";
|
|
112
|
+
return `<div part="card" class="adland-card">${image ? `<img part="media" class="adland-media" src="${escapeAttr(image)}" alt="" loading="lazy" />` : ""}<div part="body" class="adland-body">${title ? `<p part="title" class="adland-title">${escapeHtml(title)}</p>` : ""}${description ? `<p part="description" class="adland-description">${escapeHtml(description)}</p>` : ""}</div>${badge ? `<span part="badge" class="adland-badge">${escapeHtml(badge)}</span>` : ""}<span part="label" class="adland-label">Ad</span></div>`;
|
|
113
|
+
}
|
|
114
|
+
function emptyMarkup() {
|
|
115
|
+
return `<div part="card" class="adland-card adland-empty"><span part="description" class="adland-description">Your ad here</span><span part="label" class="adland-label">Ad</span></div>`;
|
|
116
|
+
}
|
|
117
|
+
function adWebUrl(ad) {
|
|
118
|
+
if (ad.type === "link" || ad.type === "miniapp") {
|
|
119
|
+
const url = ad.data.url;
|
|
120
|
+
return typeof url === "string" ? url : null;
|
|
121
|
+
}
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// src/theme.ts
|
|
126
|
+
var THEME_VARS = [
|
|
127
|
+
"--adland-bg",
|
|
128
|
+
"--adland-fg",
|
|
129
|
+
"--adland-muted",
|
|
130
|
+
"--adland-label",
|
|
131
|
+
"--adland-border",
|
|
132
|
+
"--adland-border-hover",
|
|
133
|
+
"--adland-border-width",
|
|
134
|
+
"--adland-radius",
|
|
135
|
+
"--adland-padding",
|
|
136
|
+
"--adland-gap",
|
|
137
|
+
"--adland-font",
|
|
138
|
+
"--adland-shadow-hover",
|
|
139
|
+
"--adland-media-size",
|
|
140
|
+
"--adland-media-radius",
|
|
141
|
+
"--adland-media-bg",
|
|
142
|
+
"--adland-title-size",
|
|
143
|
+
"--adland-title-weight",
|
|
144
|
+
"--adland-description-size",
|
|
145
|
+
"--adland-badge-radius",
|
|
146
|
+
"--adland-accent-bg",
|
|
147
|
+
"--adland-accent-fg"
|
|
148
|
+
];
|
|
149
|
+
function readThemeVars(host) {
|
|
150
|
+
const styles = getComputedStyle(host);
|
|
151
|
+
let out = "";
|
|
152
|
+
for (const name of THEME_VARS) {
|
|
153
|
+
const value = styles.getPropertyValue(name).trim();
|
|
154
|
+
if (value) out += `${name}:${value};`;
|
|
155
|
+
}
|
|
156
|
+
return out;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/element.ts
|
|
160
|
+
var DEFAULT_CHAIN = 8453;
|
|
161
|
+
var DEFAULT_BASE_LINK = "https://app.0xslots.org";
|
|
162
|
+
var RESERVED_HEIGHT = "68px";
|
|
163
|
+
function makeAdlandSlot(adapter) {
|
|
164
|
+
return class AdlandSlot extends HTMLElement {
|
|
165
|
+
static observedAttributes = [
|
|
166
|
+
"slot",
|
|
167
|
+
"chain",
|
|
168
|
+
"placement",
|
|
169
|
+
"render-mode",
|
|
170
|
+
"gateway",
|
|
171
|
+
"endpoint",
|
|
172
|
+
"rpc-url",
|
|
173
|
+
"consent",
|
|
174
|
+
"verified",
|
|
175
|
+
"base-link-url",
|
|
176
|
+
"data-ad"
|
|
177
|
+
];
|
|
178
|
+
shadowInited = false;
|
|
179
|
+
cleanupImpression;
|
|
180
|
+
messageHandler;
|
|
181
|
+
renderToken = 0;
|
|
182
|
+
connectedCallback() {
|
|
183
|
+
void this.render();
|
|
184
|
+
}
|
|
185
|
+
disconnectedCallback() {
|
|
186
|
+
this.teardown();
|
|
187
|
+
}
|
|
188
|
+
attributeChangedCallback() {
|
|
189
|
+
if (this.isConnected) void this.render();
|
|
190
|
+
}
|
|
191
|
+
attr(name) {
|
|
192
|
+
return this.getAttribute(name) ?? void 0;
|
|
193
|
+
}
|
|
194
|
+
chainId() {
|
|
195
|
+
return Number(this.attr("chain") ?? DEFAULT_CHAIN) || DEFAULT_CHAIN;
|
|
196
|
+
}
|
|
197
|
+
teardown() {
|
|
198
|
+
this.cleanupImpression?.();
|
|
199
|
+
this.cleanupImpression = void 0;
|
|
200
|
+
if (this.messageHandler) {
|
|
201
|
+
window.removeEventListener("message", this.messageHandler);
|
|
202
|
+
this.messageHandler = void 0;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
/** Resolve the ad: inline `data-ad` JSON, else an on-chain read. */
|
|
206
|
+
async loadAd() {
|
|
207
|
+
const staticData = this.attr("data-ad");
|
|
208
|
+
if (staticData) {
|
|
209
|
+
try {
|
|
210
|
+
return { data: JSON.parse(staticData), cid: null };
|
|
211
|
+
} catch {
|
|
212
|
+
return { data: null, cid: null };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
const slot = this.attr("slot");
|
|
216
|
+
if (!slot) return { data: null, cid: null };
|
|
217
|
+
const client = core.createReadClient(this.chainId(), this.attr("rpc-url"));
|
|
218
|
+
const uri = await core.fetchMetadataURI(client, slot);
|
|
219
|
+
if (!uri) return { data: null, cid: null };
|
|
220
|
+
return core.fetchAdFromURI(uri, this.attr("gateway"));
|
|
221
|
+
}
|
|
222
|
+
async render() {
|
|
223
|
+
const token = ++this.renderToken;
|
|
224
|
+
this.teardown();
|
|
225
|
+
if (!this.style.minHeight) this.style.minHeight = RESERVED_HEIGHT;
|
|
226
|
+
let result;
|
|
227
|
+
try {
|
|
228
|
+
result = await this.loadAd();
|
|
229
|
+
} catch {
|
|
230
|
+
result = { data: null, cid: null };
|
|
231
|
+
}
|
|
232
|
+
if (token !== this.renderToken || !this.isConnected) return;
|
|
233
|
+
const inner = result.data ? cardMarkup(result.data) : emptyMarkup();
|
|
234
|
+
const mode = this.attr("render-mode") === "inline" ? "inline" : "isolated";
|
|
235
|
+
if (mode === "inline") this.renderInline(inner, result.data);
|
|
236
|
+
else this.renderIsolated(inner, result.data);
|
|
237
|
+
const slot = this.attr("slot");
|
|
238
|
+
if (slot) {
|
|
239
|
+
const track = this.attr("verified") != null ? core.trackVerifiedImpression : core.trackImpression;
|
|
240
|
+
this.cleanupImpression = track(this, {
|
|
241
|
+
slot,
|
|
242
|
+
chainId: this.chainId(),
|
|
243
|
+
placement: this.attr("placement"),
|
|
244
|
+
cid: result.cid,
|
|
245
|
+
empty: !result.data,
|
|
246
|
+
endpoint: this.attr("endpoint"),
|
|
247
|
+
consent: this.attr("consent")
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
renderInline(inner, data) {
|
|
252
|
+
const root = this.shadowRoot ?? (this.shadowInited ? null : this.attachShadow({ mode: "open" }));
|
|
253
|
+
if (!root) return;
|
|
254
|
+
this.shadowInited = true;
|
|
255
|
+
root.innerHTML = `<style>${BASE_STYLES}</style>${inner}`;
|
|
256
|
+
const card = root.querySelector(".adland-card");
|
|
257
|
+
card?.addEventListener("click", () => this.handleClick(data, false));
|
|
258
|
+
}
|
|
259
|
+
renderIsolated(inner, data) {
|
|
260
|
+
this.replaceChildren();
|
|
261
|
+
const url = data ? adWebUrl(data) : this.emptyUrl();
|
|
262
|
+
const body = url ? `<a href="${escapeAttr(url)}" target="_blank" rel="noopener noreferrer" style="display:block;text-decoration:none;color:inherit">${inner}</a>` : inner;
|
|
263
|
+
const theme = readThemeVars(this);
|
|
264
|
+
const iframe = document.createElement("iframe");
|
|
265
|
+
iframe.setAttribute(
|
|
266
|
+
"sandbox",
|
|
267
|
+
"allow-popups allow-popups-to-escape-sandbox allow-scripts"
|
|
268
|
+
);
|
|
269
|
+
iframe.setAttribute("title", "Advertisement");
|
|
270
|
+
iframe.setAttribute("scrolling", "no");
|
|
271
|
+
iframe.style.cssText = "width:100%;border:0;display:block;height:100%";
|
|
272
|
+
iframe.srcdoc = `<!doctype html><html><head><meta charset="utf-8"><style>:root{${theme}}html,body{margin:0;padding:0}${BASE_STYLES}</style></head><body>${body}<script>(function(){function send(t,x){parent.postMessage(Object.assign({__adland:t},x||{}),'*')}addEventListener('click',function(){send('click')});function size(){send('size',{height:document.documentElement.scrollHeight})}new ResizeObserver(size).observe(document.documentElement);size()})()</script></body></html>`;
|
|
273
|
+
const handler = (e) => {
|
|
274
|
+
if (e.source !== iframe.contentWindow) return;
|
|
275
|
+
const msg = e.data;
|
|
276
|
+
if (msg?.__adland === "size" && typeof msg.height === "number") {
|
|
277
|
+
iframe.style.height = `${msg.height}px`;
|
|
278
|
+
this.style.minHeight = "";
|
|
279
|
+
} else if (msg?.__adland === "click") {
|
|
280
|
+
this.handleClick(data, true);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
this.messageHandler = handler;
|
|
284
|
+
window.addEventListener("message", handler);
|
|
285
|
+
this.appendChild(iframe);
|
|
286
|
+
}
|
|
287
|
+
baseLink() {
|
|
288
|
+
return this.attr("base-link-url") ?? DEFAULT_BASE_LINK;
|
|
289
|
+
}
|
|
290
|
+
emptyUrl() {
|
|
291
|
+
const slot = this.attr("slot");
|
|
292
|
+
if (!slot) return null;
|
|
293
|
+
return `${this.baseLink()}/slots/${slot}?chain=${this.chainId()}`;
|
|
294
|
+
}
|
|
295
|
+
handleClick(data, alreadyOpened) {
|
|
296
|
+
const slot = this.attr("slot");
|
|
297
|
+
const shared = {
|
|
298
|
+
chainId: this.chainId(),
|
|
299
|
+
placement: this.attr("placement"),
|
|
300
|
+
endpoint: this.attr("endpoint"),
|
|
301
|
+
consent: this.attr("consent")
|
|
302
|
+
};
|
|
303
|
+
if (data) {
|
|
304
|
+
if (slot) core.trackClick("click", { slot, cid: null, ...shared });
|
|
305
|
+
if (!alreadyOpened) void adapter.performAdAction(data);
|
|
306
|
+
} else if (slot) {
|
|
307
|
+
void core.trackClick("click-empty", { slot, ...shared });
|
|
308
|
+
if (!alreadyOpened) {
|
|
309
|
+
void adapter.performEmptyAdAction(
|
|
310
|
+
slot,
|
|
311
|
+
this.chainId(),
|
|
312
|
+
this.baseLink()
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// src/register.ts
|
|
321
|
+
function registerAdlandSlot(adapter, tag = "adland-slot") {
|
|
322
|
+
if (typeof customElements === "undefined") return;
|
|
323
|
+
if (customElements.get(tag)) return;
|
|
324
|
+
customElements.define(tag, makeAdlandSlot(adapter));
|
|
325
|
+
}
|
|
326
|
+
registerAdlandSlot(core.WebActionAdapter);
|
|
327
|
+
|
|
328
|
+
Object.defineProperty(exports, "WebActionAdapter", {
|
|
329
|
+
enumerable: true,
|
|
330
|
+
get: function () { return core.WebActionAdapter; }
|
|
331
|
+
});
|
|
332
|
+
exports.AD_PARTS = AD_PARTS;
|
|
333
|
+
exports.THEME_VARS = THEME_VARS;
|
|
334
|
+
exports.makeAdlandSlot = makeAdlandSlot;
|
|
335
|
+
exports.registerAdlandSlot = registerAdlandSlot;
|
|
336
|
+
//# sourceMappingURL=index.cjs.map
|
|
337
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/render.ts","../src/theme.ts","../src/element.ts","../src/register.ts","../src/index.ts"],"names":["getAdImage","getAdTitle","getAdDescription","getAdType","createReadClient","fetchMetadataURI","fetchAdFromURI","trackVerifiedImpression","trackImpression","trackClick","WebActionAdapter"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { AdData } from \"@adland/data\";\nimport {\n getAdDescription,\n getAdImage,\n getAdTitle,\n getAdType,\n} from \"@adland/react/core\";\n\n/** Part names exposed for `::part()` styling in inline (shadow DOM) mode. */\nexport const AD_PARTS = [\n \"card\",\n \"media\",\n \"body\",\n \"title\",\n \"description\",\n \"badge\",\n \"label\",\n] as const;\n\nconst TYPE_LABELS: Record<string, string> = {\n link: \"Link\",\n cast: \"Cast\",\n miniapp: \"Mini App\",\n token: \"Token\",\n farcasterProfile: \"Profile\",\n};\n\n/** Escape text for safe interpolation into HTML element content. */\nexport function escapeHtml(value: string): string {\n return value.replace(\n /[&<>\"']/g,\n (c) =>\n ({\n \"&\": \"&\",\n \"<\": \"<\",\n \">\": \">\",\n '\"': \""\",\n \"'\": \"'\",\n })[c] as string,\n );\n}\n\n/** Escape a value for use inside a double-quoted HTML attribute. */\nexport function escapeAttr(value: string): string {\n return value.replace(/\"/g, \""\").replace(/</g, \"<\");\n}\n\n/**\n * Default ad-card styles. Every visual is driven by a `--adland-*` custom\n * property with a sensible fallback, so a publisher can re-theme the card\n * without overriding a single rule. Custom properties inherit through the\n * shadow boundary (inline mode) and are copied into the iframe (isolated mode).\n */\nexport const BASE_STYLES = `\n:host { display: block; }\n* { box-sizing: border-box; }\n.adland-card {\n position: relative;\n display: flex;\n gap: var(--adland-gap, 12px);\n align-items: center;\n padding: var(--adland-padding, 12px);\n border: var(--adland-border-width, 1px) solid var(--adland-border, #e5e7eb);\n border-radius: var(--adland-radius, 12px);\n background: var(--adland-bg, #ffffff);\n color: var(--adland-fg, #111827);\n font-family: var(--adland-font, system-ui, -apple-system, sans-serif);\n cursor: pointer;\n text-decoration: none;\n transition: border-color 0.15s ease, box-shadow 0.15s ease;\n}\n.adland-card:hover {\n border-color: var(--adland-border-hover, #d1d5db);\n box-shadow: var(--adland-shadow-hover, 0 1px 3px rgba(0, 0, 0, 0.08));\n}\n.adland-media {\n width: var(--adland-media-size, 44px);\n height: var(--adland-media-size, 44px);\n border-radius: var(--adland-media-radius, 8px);\n object-fit: cover;\n flex: none;\n background: var(--adland-media-bg, #f3f4f6);\n}\n.adland-body { min-width: 0; flex: 1; }\n.adland-title {\n margin: 0;\n font-size: var(--adland-title-size, 14px);\n font-weight: var(--adland-title-weight, 600);\n line-height: 1.3;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.adland-description {\n margin: 2px 0 0;\n font-size: var(--adland-description-size, 12px);\n color: var(--adland-muted, #6b7280);\n line-height: 1.4;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n.adland-badge {\n flex: none;\n font-size: 10px;\n font-weight: 600;\n padding: 2px 6px;\n border-radius: var(--adland-badge-radius, 6px);\n background: var(--adland-accent-bg, #eef2ff);\n color: var(--adland-accent-fg, #4338ca);\n}\n.adland-label {\n position: absolute;\n top: 6px;\n right: 8px;\n font-size: 9px;\n letter-spacing: 0.04em;\n text-transform: uppercase;\n color: var(--adland-label, #9ca3af);\n}\n.adland-empty { justify-content: center; color: var(--adland-muted, #6b7280); }\n`;\n\n/** Inner card markup (parts + classes) for a loaded ad. */\nexport function cardMarkup(ad: AdData): string {\n const image = getAdImage(ad);\n const title = getAdTitle(ad);\n const description = getAdDescription(ad);\n const type = getAdType(ad);\n const badge = type ? (TYPE_LABELS[type] ?? type) : \"\";\n\n return `<div part=\"card\" class=\"adland-card\">${\n image\n ? `<img part=\"media\" class=\"adland-media\" src=\"${escapeAttr(image)}\" alt=\"\" loading=\"lazy\" />`\n : \"\"\n }<div part=\"body\" class=\"adland-body\">${\n title ? `<p part=\"title\" class=\"adland-title\">${escapeHtml(title)}</p>` : \"\"\n }${\n description\n ? `<p part=\"description\" class=\"adland-description\">${escapeHtml(description)}</p>`\n : \"\"\n }</div>${\n badge\n ? `<span part=\"badge\" class=\"adland-badge\">${escapeHtml(badge)}</span>`\n : \"\"\n }<span part=\"label\" class=\"adland-label\">Ad</span></div>`;\n}\n\n/** Inner card markup for an empty slot (the \"Your ad here\" CTA). */\nexport function emptyMarkup(): string {\n return `<div part=\"card\" class=\"adland-card adland-empty\"><span part=\"description\" class=\"adland-description\">Your ad here</span><span part=\"label\" class=\"adland-label\">Ad</span></div>`;\n}\n\n/** The external URL an ad maps to on the web, or null if it has none. */\nexport function adWebUrl(ad: AdData): string | null {\n if (ad.type === \"link\" || ad.type === \"miniapp\") {\n const url = (ad.data as { url?: unknown }).url;\n return typeof url === \"string\" ? url : null;\n }\n return null;\n}\n","/**\n * The `--adland-*` custom properties the card reads. Custom properties inherit\n * through the shadow boundary automatically (inline mode), but NOT across an\n * iframe document boundary — so for isolated mode we read whichever of these\n * are set on the host and copy them into the iframe's `:root`.\n */\nexport const THEME_VARS = [\n \"--adland-bg\",\n \"--adland-fg\",\n \"--adland-muted\",\n \"--adland-label\",\n \"--adland-border\",\n \"--adland-border-hover\",\n \"--adland-border-width\",\n \"--adland-radius\",\n \"--adland-padding\",\n \"--adland-gap\",\n \"--adland-font\",\n \"--adland-shadow-hover\",\n \"--adland-media-size\",\n \"--adland-media-radius\",\n \"--adland-media-bg\",\n \"--adland-title-size\",\n \"--adland-title-weight\",\n \"--adland-description-size\",\n \"--adland-badge-radius\",\n \"--adland-accent-bg\",\n \"--adland-accent-fg\",\n] as const;\n\n/**\n * Serialize the theme custom properties currently set on `host` into a CSS\n * declaration string (e.g. `--adland-bg:#000;`). Empty when none are set.\n */\nexport function readThemeVars(host: Element): string {\n const styles = getComputedStyle(host);\n let out = \"\";\n for (const name of THEME_VARS) {\n const value = styles.getPropertyValue(name).trim();\n if (value) out += `${name}:${value};`;\n }\n return out;\n}\n","import type { AdData } from \"@adland/data\";\nimport {\n type ActionAdapter,\n type AdConsent,\n createReadClient,\n fetchAdFromURI,\n fetchMetadataURI,\n trackClick,\n trackImpression,\n trackVerifiedImpression,\n} from \"@adland/react/core\";\nimport {\n adWebUrl,\n BASE_STYLES,\n cardMarkup,\n emptyMarkup,\n escapeAttr,\n} from \"./render\";\nimport { readThemeVars } from \"./theme\";\n\nconst DEFAULT_CHAIN = 8453;\nconst DEFAULT_BASE_LINK = \"https://app.0xslots.org\";\nconst RESERVED_HEIGHT = \"68px\";\n\n/**\n * Build the `<adland-slot>` custom-element class bound to a click-routing\n * adapter. The web CDN bundle binds the web adapter (no Farcaster); the\n * Farcaster bundle binds the Mini App adapter.\n */\nexport function makeAdlandSlot(\n adapter: ActionAdapter,\n): CustomElementConstructor {\n return class AdlandSlot extends HTMLElement {\n static observedAttributes = [\n \"slot\",\n \"chain\",\n \"placement\",\n \"render-mode\",\n \"gateway\",\n \"endpoint\",\n \"rpc-url\",\n \"consent\",\n \"verified\",\n \"base-link-url\",\n \"data-ad\",\n ];\n\n private shadowInited = false;\n private cleanupImpression?: () => void;\n private messageHandler?: (e: MessageEvent) => void;\n private renderToken = 0;\n\n connectedCallback() {\n void this.render();\n }\n\n disconnectedCallback() {\n this.teardown();\n }\n\n attributeChangedCallback() {\n if (this.isConnected) void this.render();\n }\n\n private attr(name: string): string | undefined {\n return this.getAttribute(name) ?? undefined;\n }\n\n private chainId(): number {\n return Number(this.attr(\"chain\") ?? DEFAULT_CHAIN) || DEFAULT_CHAIN;\n }\n\n private teardown() {\n this.cleanupImpression?.();\n this.cleanupImpression = undefined;\n if (this.messageHandler) {\n window.removeEventListener(\"message\", this.messageHandler);\n this.messageHandler = undefined;\n }\n }\n\n /** Resolve the ad: inline `data-ad` JSON, else an on-chain read. */\n private async loadAd(): Promise<{\n data: AdData | null;\n cid: string | null;\n }> {\n const staticData = this.attr(\"data-ad\");\n if (staticData) {\n try {\n return { data: JSON.parse(staticData) as AdData, cid: null };\n } catch {\n return { data: null, cid: null };\n }\n }\n\n const slot = this.attr(\"slot\");\n if (!slot) return { data: null, cid: null };\n\n const client = createReadClient(this.chainId(), this.attr(\"rpc-url\"));\n const uri = await fetchMetadataURI(client, slot);\n if (!uri) return { data: null, cid: null };\n return fetchAdFromURI(uri, this.attr(\"gateway\"));\n }\n\n private async render() {\n const token = ++this.renderToken;\n this.teardown();\n\n // Reserve height up front so an async load doesn't shift the layout.\n if (!this.style.minHeight) this.style.minHeight = RESERVED_HEIGHT;\n\n let result: { data: AdData | null; cid: string | null };\n try {\n result = await this.loadAd();\n } catch {\n result = { data: null, cid: null };\n }\n // A newer render superseded this one while awaiting — bail.\n if (token !== this.renderToken || !this.isConnected) return;\n\n const inner = result.data ? cardMarkup(result.data) : emptyMarkup();\n const mode =\n this.attr(\"render-mode\") === \"inline\" ? \"inline\" : \"isolated\";\n if (mode === \"inline\") this.renderInline(inner, result.data);\n else this.renderIsolated(inner, result.data);\n\n const slot = this.attr(\"slot\");\n if (slot) {\n const track =\n this.attr(\"verified\") != null\n ? trackVerifiedImpression\n : trackImpression;\n this.cleanupImpression = track(this, {\n slot,\n chainId: this.chainId(),\n placement: this.attr(\"placement\"),\n cid: result.cid,\n empty: !result.data,\n endpoint: this.attr(\"endpoint\"),\n consent: this.attr(\"consent\") as AdConsent | undefined,\n });\n }\n }\n\n private renderInline(inner: string, data: AdData | null) {\n const root =\n this.shadowRoot ??\n (this.shadowInited ? null : this.attachShadow({ mode: \"open\" }));\n if (!root) return;\n this.shadowInited = true;\n root.innerHTML = `<style>${BASE_STYLES}</style>${inner}`;\n const card = root.querySelector<HTMLElement>(\".adland-card\");\n card?.addEventListener(\"click\", () => this.handleClick(data, false));\n }\n\n private renderIsolated(inner: string, data: AdData | null) {\n this.replaceChildren();\n\n const url = data ? adWebUrl(data) : this.emptyUrl();\n const body = url\n ? `<a href=\"${escapeAttr(url)}\" target=\"_blank\" rel=\"noopener noreferrer\" style=\"display:block;text-decoration:none;color:inherit\">${inner}</a>`\n : inner;\n const theme = readThemeVars(this);\n\n const iframe = document.createElement(\"iframe\");\n iframe.setAttribute(\n \"sandbox\",\n \"allow-popups allow-popups-to-escape-sandbox allow-scripts\",\n );\n iframe.setAttribute(\"title\", \"Advertisement\");\n iframe.setAttribute(\"scrolling\", \"no\");\n iframe.style.cssText = \"width:100%;border:0;display:block;height:100%\";\n iframe.srcdoc = `<!doctype html><html><head><meta charset=\"utf-8\"><style>:root{${theme}}html,body{margin:0;padding:0}${BASE_STYLES}</style></head><body>${body}<script>(function(){function send(t,x){parent.postMessage(Object.assign({__adland:t},x||{}),'*')}addEventListener('click',function(){send('click')});function size(){send('size',{height:document.documentElement.scrollHeight})}new ResizeObserver(size).observe(document.documentElement);size()})()</script></body></html>`;\n\n const handler = (e: MessageEvent) => {\n if (e.source !== iframe.contentWindow) return;\n const msg = e.data as { __adland?: string; height?: number };\n if (msg?.__adland === \"size\" && typeof msg.height === \"number\") {\n iframe.style.height = `${msg.height}px`;\n this.style.minHeight = \"\";\n } else if (msg?.__adland === \"click\") {\n // The iframe's anchor already opened the URL — only track here.\n this.handleClick(data, true);\n }\n };\n this.messageHandler = handler;\n window.addEventListener(\"message\", handler);\n this.appendChild(iframe);\n }\n\n private baseLink(): string {\n return this.attr(\"base-link-url\") ?? DEFAULT_BASE_LINK;\n }\n\n private emptyUrl(): string | null {\n const slot = this.attr(\"slot\");\n if (!slot) return null;\n return `${this.baseLink()}/slots/${slot}?chain=${this.chainId()}`;\n }\n\n private handleClick(data: AdData | null, alreadyOpened: boolean) {\n const slot = this.attr(\"slot\");\n const shared = {\n chainId: this.chainId(),\n placement: this.attr(\"placement\"),\n endpoint: this.attr(\"endpoint\"),\n consent: this.attr(\"consent\") as AdConsent | undefined,\n };\n\n if (data) {\n if (slot) trackClick(\"click\", { slot, cid: null, ...shared });\n if (!alreadyOpened) void adapter.performAdAction(data);\n } else if (slot) {\n void trackClick(\"click-empty\", { slot, ...shared });\n if (!alreadyOpened) {\n void adapter.performEmptyAdAction(\n slot,\n this.chainId(),\n this.baseLink(),\n );\n }\n }\n }\n };\n}\n","import type { ActionAdapter } from \"@adland/react/core\";\nimport { makeAdlandSlot } from \"./element\";\n\n/**\n * Define the `<adland-slot>` custom element bound to a click-routing adapter.\n * Safe to call more than once and in non-DOM environments — it no-ops if the\n * element is already defined or `customElements` is unavailable (SSR).\n */\nexport function registerAdlandSlot(\n adapter: ActionAdapter,\n tag = \"adland-slot\",\n): void {\n if (typeof customElements === \"undefined\") return;\n if (customElements.get(tag)) return;\n customElements.define(tag, makeAdlandSlot(adapter));\n}\n","import { WebActionAdapter } from \"@adland/react/core\";\nimport { registerAdlandSlot } from \"./register\";\n\nexport { WebActionAdapter } from \"@adland/react/core\";\nexport { makeAdlandSlot } from \"./element\";\nexport { registerAdlandSlot } from \"./register\";\nexport { AD_PARTS } from \"./render\";\nexport { THEME_VARS } from \"./theme\";\n\n// Convenience: importing the package auto-registers <adland-slot> with the web\n// adapter (no Farcaster). For Farcaster routing, call registerAdlandSlot with\n// FarcasterActionAdapter from \"@adland/react/farcaster\" instead.\nregisterAdlandSlot(WebActionAdapter);\n"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ActionAdapter } from '@adland/react/core';
|
|
2
|
+
export { WebActionAdapter } from '@adland/react/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Build the `<adland-slot>` custom-element class bound to a click-routing
|
|
6
|
+
* adapter. The web CDN bundle binds the web adapter (no Farcaster); the
|
|
7
|
+
* Farcaster bundle binds the Mini App adapter.
|
|
8
|
+
*/
|
|
9
|
+
declare function makeAdlandSlot(adapter: ActionAdapter): CustomElementConstructor;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Define the `<adland-slot>` custom element bound to a click-routing adapter.
|
|
13
|
+
* Safe to call more than once and in non-DOM environments — it no-ops if the
|
|
14
|
+
* element is already defined or `customElements` is unavailable (SSR).
|
|
15
|
+
*/
|
|
16
|
+
declare function registerAdlandSlot(adapter: ActionAdapter, tag?: string): void;
|
|
17
|
+
|
|
18
|
+
/** Part names exposed for `::part()` styling in inline (shadow DOM) mode. */
|
|
19
|
+
declare const AD_PARTS: readonly ["card", "media", "body", "title", "description", "badge", "label"];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The `--adland-*` custom properties the card reads. Custom properties inherit
|
|
23
|
+
* through the shadow boundary automatically (inline mode), but NOT across an
|
|
24
|
+
* iframe document boundary — so for isolated mode we read whichever of these
|
|
25
|
+
* are set on the host and copy them into the iframe's `:root`.
|
|
26
|
+
*/
|
|
27
|
+
declare const THEME_VARS: readonly ["--adland-bg", "--adland-fg", "--adland-muted", "--adland-label", "--adland-border", "--adland-border-hover", "--adland-border-width", "--adland-radius", "--adland-padding", "--adland-gap", "--adland-font", "--adland-shadow-hover", "--adland-media-size", "--adland-media-radius", "--adland-media-bg", "--adland-title-size", "--adland-title-weight", "--adland-description-size", "--adland-badge-radius", "--adland-accent-bg", "--adland-accent-fg"];
|
|
28
|
+
|
|
29
|
+
export { AD_PARTS, THEME_VARS, makeAdlandSlot, registerAdlandSlot };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ActionAdapter } from '@adland/react/core';
|
|
2
|
+
export { WebActionAdapter } from '@adland/react/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Build the `<adland-slot>` custom-element class bound to a click-routing
|
|
6
|
+
* adapter. The web CDN bundle binds the web adapter (no Farcaster); the
|
|
7
|
+
* Farcaster bundle binds the Mini App adapter.
|
|
8
|
+
*/
|
|
9
|
+
declare function makeAdlandSlot(adapter: ActionAdapter): CustomElementConstructor;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Define the `<adland-slot>` custom element bound to a click-routing adapter.
|
|
13
|
+
* Safe to call more than once and in non-DOM environments — it no-ops if the
|
|
14
|
+
* element is already defined or `customElements` is unavailable (SSR).
|
|
15
|
+
*/
|
|
16
|
+
declare function registerAdlandSlot(adapter: ActionAdapter, tag?: string): void;
|
|
17
|
+
|
|
18
|
+
/** Part names exposed for `::part()` styling in inline (shadow DOM) mode. */
|
|
19
|
+
declare const AD_PARTS: readonly ["card", "media", "body", "title", "description", "badge", "label"];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The `--adland-*` custom properties the card reads. Custom properties inherit
|
|
23
|
+
* through the shadow boundary automatically (inline mode), but NOT across an
|
|
24
|
+
* iframe document boundary — so for isolated mode we read whichever of these
|
|
25
|
+
* are set on the host and copy them into the iframe's `:root`.
|
|
26
|
+
*/
|
|
27
|
+
declare const THEME_VARS: readonly ["--adland-bg", "--adland-fg", "--adland-muted", "--adland-label", "--adland-border", "--adland-border-hover", "--adland-border-width", "--adland-radius", "--adland-padding", "--adland-gap", "--adland-font", "--adland-shadow-hover", "--adland-media-size", "--adland-media-radius", "--adland-media-bg", "--adland-title-size", "--adland-title-weight", "--adland-description-size", "--adland-badge-radius", "--adland-accent-bg", "--adland-accent-fg"];
|
|
28
|
+
|
|
29
|
+
export { AD_PARTS, THEME_VARS, makeAdlandSlot, registerAdlandSlot };
|