@adland/embed 0.2.3 → 0.3.0
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 +30 -0
- package/dist/adland-farcaster.js +29 -29
- package/dist/adland-farcaster.js.map +1 -1
- package/dist/adland.js +25 -25
- package/dist/adland.js.map +1 -1
- package/dist/index.cjs +24 -6
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +25 -7
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -177,6 +177,15 @@ function makeAdlandSlot(adapter) {
|
|
|
177
177
|
];
|
|
178
178
|
shadowInited = false;
|
|
179
179
|
cleanupImpression;
|
|
180
|
+
/**
|
|
181
|
+
* The address this element is actually showing.
|
|
182
|
+
*
|
|
183
|
+
* The `slot` attribute when there is one, otherwise whatever `slot-key`
|
|
184
|
+
* resolved to. Everything below the fetch needs an address — the impression
|
|
185
|
+
* is recorded against one and the empty-slot CTA links to one — and a name
|
|
186
|
+
* is not an address until the module has answered.
|
|
187
|
+
*/
|
|
188
|
+
resolvedSlot;
|
|
180
189
|
messageHandler;
|
|
181
190
|
renderToken = 0;
|
|
182
191
|
connectedCallback() {
|
|
@@ -213,8 +222,16 @@ function makeAdlandSlot(adapter) {
|
|
|
213
222
|
}
|
|
214
223
|
}
|
|
215
224
|
const slot = this.attr("slot");
|
|
216
|
-
|
|
225
|
+
const slotKey = this.attr("slot-key");
|
|
226
|
+
if (!slot && !slotKey) return { data: null, cid: null };
|
|
217
227
|
const client = core.createReadClient(this.chainId(), this.attr("rpc-url"));
|
|
228
|
+
if (!slot && slotKey) {
|
|
229
|
+
const snap = await core.fetchAdByKey(client, slotKey, {
|
|
230
|
+
gateway: this.attr("gateway"),
|
|
231
|
+
adModule: this.attr("ad-module")
|
|
232
|
+
});
|
|
233
|
+
return snap ? { data: snap.data, cid: snap.cid, slot: snap.slot } : { data: null, cid: null };
|
|
234
|
+
}
|
|
218
235
|
const uri = await core.fetchMetadataURI(client, slot);
|
|
219
236
|
if (!uri) return { data: null, cid: null };
|
|
220
237
|
return core.fetchAdFromURI(uri, this.attr("gateway"));
|
|
@@ -234,11 +251,12 @@ function makeAdlandSlot(adapter) {
|
|
|
234
251
|
const mode = this.attr("render-mode") === "inline" ? "inline" : "isolated";
|
|
235
252
|
if (mode === "inline") this.renderInline(inner, result.data);
|
|
236
253
|
else this.renderIsolated(inner, result.data);
|
|
237
|
-
const
|
|
238
|
-
|
|
254
|
+
const resolved = this.attr("slot") ?? result.slot;
|
|
255
|
+
this.resolvedSlot = resolved;
|
|
256
|
+
if (resolved) {
|
|
239
257
|
const track = this.attr("verified") != null ? core.trackVerifiedImpression : core.trackImpression;
|
|
240
258
|
this.cleanupImpression = track(this, {
|
|
241
|
-
slot,
|
|
259
|
+
slot: resolved,
|
|
242
260
|
chainId: this.chainId(),
|
|
243
261
|
placement: this.attr("placement"),
|
|
244
262
|
cid: result.cid,
|
|
@@ -288,12 +306,12 @@ function makeAdlandSlot(adapter) {
|
|
|
288
306
|
return this.attr("base-link-url") ?? DEFAULT_BASE_LINK;
|
|
289
307
|
}
|
|
290
308
|
emptyUrl() {
|
|
291
|
-
const slot = this.attr("slot");
|
|
309
|
+
const slot = this.resolvedSlot ?? this.attr("slot");
|
|
292
310
|
if (!slot) return null;
|
|
293
311
|
return `${this.baseLink()}/slots/${slot}?chain=${this.chainId()}`;
|
|
294
312
|
}
|
|
295
313
|
handleClick(data, alreadyOpened) {
|
|
296
|
-
const slot = this.attr("slot");
|
|
314
|
+
const slot = this.resolvedSlot ?? this.attr("slot");
|
|
297
315
|
const shared = {
|
|
298
316
|
chainId: this.chainId(),
|
|
299
317
|
placement: this.attr("placement"),
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +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;\n/**\n * Where an unsold slot sends a reader.\n *\n * The 0xSlots app moved off its own subdomain and now lives under a path on the\n * apex domain, so the base carries `/app` and the slot path is appended to it.\n */\nconst DEFAULT_BASE_LINK = \"https://0xslots.org/app\";\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"]}
|
|
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","fetchAdByKey","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 fetchAdByKey,\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;\n/**\n * Where an unsold slot sends a reader.\n *\n * The 0xSlots app moved off its own subdomain and now lives under a path on the\n * apex domain, so the base carries `/app` and the slot path is appended to it.\n */\nconst DEFAULT_BASE_LINK = \"https://0xslots.org/app\";\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 /**\n * The address this element is actually showing.\n *\n * The `slot` attribute when there is one, otherwise whatever `slot-key`\n * resolved to. Everything below the fetch needs an address — the impression\n * is recorded against one and the empty-slot CTA links to one — and a name\n * is not an address until the module has answered.\n */\n private resolvedSlot?: string;\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 /** Set when a NAME was resolved; the address it pointed at. */\n slot?: string;\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 const slotKey = this.attr(\"slot-key\");\n if (!slot && !slotKey) return { data: null, cid: null };\n\n const client = createReadClient(this.chainId(), this.attr(\"rpc-url\"));\n\n // A NAME, resolved through the metadata module, in one call.\n //\n // Preferred over an address for the reason the whole registry exists: an\n // address pasted into a page is one nobody can ever change, so redeploying\n // a slot leaves every embed carrying it showing a dead space. `slot` still\n // wins when both are set, because a publisher who pinned an address did so\n // to opt out of trusting us with the pointer.\n if (!slot && slotKey) {\n const snap = await fetchAdByKey(client, slotKey, {\n gateway: this.attr(\"gateway\"),\n adModule: this.attr(\"ad-module\"),\n });\n // The resolved address travels with it: the impression below is\n // recorded against a slot, and a name is not one.\n return snap\n ? { data: snap.data, cid: snap.cid, slot: snap.slot }\n : { data: null, cid: null };\n }\n\n const uri = await fetchMetadataURI(client, slot as string);\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; slot?: string };\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 // The attribute if there was one, otherwise the address the name\n // resolved to. Recording an impression needs an address either way.\n const resolved = this.attr(\"slot\") ?? result.slot;\n this.resolvedSlot = resolved;\n if (resolved) {\n const track =\n this.attr(\"verified\") != null\n ? trackVerifiedImpression\n : trackImpression;\n this.cleanupImpression = track(this, {\n slot: resolved,\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.resolvedSlot ?? 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.resolvedSlot ?? 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.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createReadClient, fetchMetadataURI, fetchAdFromURI, trackVerifiedImpression, trackImpression, trackClick, getAdImage, getAdTitle, getAdDescription, getAdType, WebActionAdapter } from '@adland/react/core';
|
|
1
|
+
import { createReadClient, fetchAdByKey, fetchMetadataURI, fetchAdFromURI, trackVerifiedImpression, trackImpression, trackClick, getAdImage, getAdTitle, getAdDescription, getAdType, WebActionAdapter } from '@adland/react/core';
|
|
2
2
|
export { WebActionAdapter } from '@adland/react/core';
|
|
3
3
|
|
|
4
4
|
// src/index.ts
|
|
@@ -176,6 +176,15 @@ function makeAdlandSlot(adapter) {
|
|
|
176
176
|
];
|
|
177
177
|
shadowInited = false;
|
|
178
178
|
cleanupImpression;
|
|
179
|
+
/**
|
|
180
|
+
* The address this element is actually showing.
|
|
181
|
+
*
|
|
182
|
+
* The `slot` attribute when there is one, otherwise whatever `slot-key`
|
|
183
|
+
* resolved to. Everything below the fetch needs an address — the impression
|
|
184
|
+
* is recorded against one and the empty-slot CTA links to one — and a name
|
|
185
|
+
* is not an address until the module has answered.
|
|
186
|
+
*/
|
|
187
|
+
resolvedSlot;
|
|
179
188
|
messageHandler;
|
|
180
189
|
renderToken = 0;
|
|
181
190
|
connectedCallback() {
|
|
@@ -212,8 +221,16 @@ function makeAdlandSlot(adapter) {
|
|
|
212
221
|
}
|
|
213
222
|
}
|
|
214
223
|
const slot = this.attr("slot");
|
|
215
|
-
|
|
224
|
+
const slotKey = this.attr("slot-key");
|
|
225
|
+
if (!slot && !slotKey) return { data: null, cid: null };
|
|
216
226
|
const client = createReadClient(this.chainId(), this.attr("rpc-url"));
|
|
227
|
+
if (!slot && slotKey) {
|
|
228
|
+
const snap = await fetchAdByKey(client, slotKey, {
|
|
229
|
+
gateway: this.attr("gateway"),
|
|
230
|
+
adModule: this.attr("ad-module")
|
|
231
|
+
});
|
|
232
|
+
return snap ? { data: snap.data, cid: snap.cid, slot: snap.slot } : { data: null, cid: null };
|
|
233
|
+
}
|
|
217
234
|
const uri = await fetchMetadataURI(client, slot);
|
|
218
235
|
if (!uri) return { data: null, cid: null };
|
|
219
236
|
return fetchAdFromURI(uri, this.attr("gateway"));
|
|
@@ -233,11 +250,12 @@ function makeAdlandSlot(adapter) {
|
|
|
233
250
|
const mode = this.attr("render-mode") === "inline" ? "inline" : "isolated";
|
|
234
251
|
if (mode === "inline") this.renderInline(inner, result.data);
|
|
235
252
|
else this.renderIsolated(inner, result.data);
|
|
236
|
-
const
|
|
237
|
-
|
|
253
|
+
const resolved = this.attr("slot") ?? result.slot;
|
|
254
|
+
this.resolvedSlot = resolved;
|
|
255
|
+
if (resolved) {
|
|
238
256
|
const track = this.attr("verified") != null ? trackVerifiedImpression : trackImpression;
|
|
239
257
|
this.cleanupImpression = track(this, {
|
|
240
|
-
slot,
|
|
258
|
+
slot: resolved,
|
|
241
259
|
chainId: this.chainId(),
|
|
242
260
|
placement: this.attr("placement"),
|
|
243
261
|
cid: result.cid,
|
|
@@ -287,12 +305,12 @@ function makeAdlandSlot(adapter) {
|
|
|
287
305
|
return this.attr("base-link-url") ?? DEFAULT_BASE_LINK;
|
|
288
306
|
}
|
|
289
307
|
emptyUrl() {
|
|
290
|
-
const slot = this.attr("slot");
|
|
308
|
+
const slot = this.resolvedSlot ?? this.attr("slot");
|
|
291
309
|
if (!slot) return null;
|
|
292
310
|
return `${this.baseLink()}/slots/${slot}?chain=${this.chainId()}`;
|
|
293
311
|
}
|
|
294
312
|
handleClick(data, alreadyOpened) {
|
|
295
|
-
const slot = this.attr("slot");
|
|
313
|
+
const slot = this.resolvedSlot ?? this.attr("slot");
|
|
296
314
|
const shared = {
|
|
297
315
|
chainId: this.chainId(),
|
|
298
316
|
placement: this.attr("placement"),
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/render.ts","../src/theme.ts","../src/element.ts","../src/register.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","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;\n/**\n * Where an unsold slot sends a reader.\n *\n * The 0xSlots app moved off its own subdomain and now lives under a path on the\n * apex domain, so the base carries `/app` and the slot path is appended to it.\n */\nconst DEFAULT_BASE_LINK = \"https://0xslots.org/app\";\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"]}
|
|
1
|
+
{"version":3,"sources":["../src/render.ts","../src/theme.ts","../src/element.ts","../src/register.ts","../src/index.ts"],"names":[],"mappings":"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","file":"index.js","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 fetchAdByKey,\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;\n/**\n * Where an unsold slot sends a reader.\n *\n * The 0xSlots app moved off its own subdomain and now lives under a path on the\n * apex domain, so the base carries `/app` and the slot path is appended to it.\n */\nconst DEFAULT_BASE_LINK = \"https://0xslots.org/app\";\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 /**\n * The address this element is actually showing.\n *\n * The `slot` attribute when there is one, otherwise whatever `slot-key`\n * resolved to. Everything below the fetch needs an address — the impression\n * is recorded against one and the empty-slot CTA links to one — and a name\n * is not an address until the module has answered.\n */\n private resolvedSlot?: string;\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 /** Set when a NAME was resolved; the address it pointed at. */\n slot?: string;\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 const slotKey = this.attr(\"slot-key\");\n if (!slot && !slotKey) return { data: null, cid: null };\n\n const client = createReadClient(this.chainId(), this.attr(\"rpc-url\"));\n\n // A NAME, resolved through the metadata module, in one call.\n //\n // Preferred over an address for the reason the whole registry exists: an\n // address pasted into a page is one nobody can ever change, so redeploying\n // a slot leaves every embed carrying it showing a dead space. `slot` still\n // wins when both are set, because a publisher who pinned an address did so\n // to opt out of trusting us with the pointer.\n if (!slot && slotKey) {\n const snap = await fetchAdByKey(client, slotKey, {\n gateway: this.attr(\"gateway\"),\n adModule: this.attr(\"ad-module\"),\n });\n // The resolved address travels with it: the impression below is\n // recorded against a slot, and a name is not one.\n return snap\n ? { data: snap.data, cid: snap.cid, slot: snap.slot }\n : { data: null, cid: null };\n }\n\n const uri = await fetchMetadataURI(client, slot as string);\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; slot?: string };\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 // The attribute if there was one, otherwise the address the name\n // resolved to. Recording an impression needs an address either way.\n const resolved = this.attr(\"slot\") ?? result.slot;\n this.resolvedSlot = resolved;\n if (resolved) {\n const track =\n this.attr(\"verified\") != null\n ? trackVerifiedImpression\n : trackImpression;\n this.cleanupImpression = track(this, {\n slot: resolved,\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.resolvedSlot ?? 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.resolvedSlot ?? 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adland/embed",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -27,8 +27,8 @@
|
|
|
27
27
|
"dependencies": {
|
|
28
28
|
"@0xslots/sdk": "^0.27.0",
|
|
29
29
|
"viem": "^2.0.0",
|
|
30
|
-
"@adland/data": "0.
|
|
31
|
-
"@adland/react": "0.23.
|
|
30
|
+
"@adland/data": "0.20.0",
|
|
31
|
+
"@adland/react": "0.23.1"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"@types/node": "^22",
|