@adland/embed 0.4.0 → 0.5.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/dist/index.cjs CHANGED
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var core = require('@adland/react/core');
4
+ var data = require('@adland/data');
4
5
 
5
6
  // src/index.ts
6
7
  var AD_PARTS = [
@@ -9,16 +10,8 @@ var AD_PARTS = [
9
10
  "body",
10
11
  "title",
11
12
  "description",
12
- "badge",
13
13
  "label"
14
14
  ];
15
- var TYPE_LABELS = {
16
- link: "Link",
17
- cast: "Cast",
18
- miniapp: "Mini App",
19
- token: "Token",
20
- farcasterProfile: "Profile"
21
- };
22
15
  function escapeHtml(value) {
23
16
  return value.replace(
24
17
  /[&<>"']/g,
@@ -50,11 +43,25 @@ var BASE_STYLES = `
50
43
  font-family: var(--adland-font, system-ui, -apple-system, sans-serif);
51
44
  cursor: pointer;
52
45
  text-decoration: none;
53
- transition: border-color 0.15s ease, box-shadow 0.15s ease;
46
+ /* Resting shadow, not just a hover one. Only --adland-shadow-hover existed,
47
+ which quietly ruled out every style whose card is raised at rest: a hard
48
+ offset shadow is most of what makes a neo-brutalist card look like one, and
49
+ there was no way to ask for it. Defaults to none, so nothing moves. */
50
+ box-shadow: var(--adland-shadow, none);
51
+ transition: var(
52
+ --adland-transition,
53
+ border-color 0.15s ease,
54
+ box-shadow 0.15s ease,
55
+ transform 0.15s ease
56
+ );
54
57
  }
55
58
  .adland-card:hover {
56
59
  border-color: var(--adland-border-hover, #d1d5db);
57
60
  box-shadow: var(--adland-shadow-hover, 0 1px 3px rgba(0, 0, 0, 0.08));
61
+ /* The other half of a raised card: shrink the shadow AND move the card into
62
+ the space it vacated, or the card appears to grow rather than press. No
63
+ default, so a theme that sets neither is untouched. */
64
+ transform: var(--adland-transform-hover, none);
58
65
  }
59
66
  .adland-media {
60
67
  width: var(--adland-media-size, 44px);
@@ -63,6 +70,7 @@ var BASE_STYLES = `
63
70
  object-fit: cover;
64
71
  flex: none;
65
72
  background: var(--adland-media-bg, #f3f4f6);
73
+ border: var(--adland-media-border, none);
66
74
  }
67
75
  .adland-body { min-width: 0; flex: 1; }
68
76
  .adland-title {
@@ -83,20 +91,11 @@ var BASE_STYLES = `
83
91
  text-overflow: ellipsis;
84
92
  white-space: nowrap;
85
93
  }
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
94
  .adland-label {
96
95
  position: absolute;
97
96
  top: 6px;
98
97
  right: 8px;
99
- font-size: 9px;
98
+ font-size: var(--adland-label-size, 9px);
100
99
  letter-spacing: 0.04em;
101
100
  text-transform: uppercase;
102
101
  color: var(--adland-label, #9ca3af);
@@ -104,22 +103,16 @@ var BASE_STYLES = `
104
103
  .adland-empty { justify-content: center; color: var(--adland-muted, #6b7280); }
105
104
  `;
106
105
  function cardMarkup(ad) {
107
- const image = core.getAdImage(ad);
106
+ const image = core.getAdIcon(ad);
108
107
  const title = core.getAdTitle(ad);
109
108
  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>`;
109
+ 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><span part="label" class="adland-label">Ad</span></div>`;
113
110
  }
114
111
  function emptyMarkup() {
115
112
  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
113
  }
117
114
  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;
115
+ return data.adDestination(ad);
123
116
  }
124
117
 
125
118
  // src/theme.ts
@@ -133,18 +126,21 @@ var THEME_VARS = [
133
126
  "--adland-border-width",
134
127
  "--adland-radius",
135
128
  "--adland-padding",
129
+ "--adland-bleed",
136
130
  "--adland-gap",
137
131
  "--adland-font",
132
+ "--adland-shadow",
138
133
  "--adland-shadow-hover",
134
+ "--adland-transform-hover",
135
+ "--adland-transition",
139
136
  "--adland-media-size",
140
137
  "--adland-media-radius",
141
138
  "--adland-media-bg",
142
139
  "--adland-title-size",
143
140
  "--adland-title-weight",
144
141
  "--adland-description-size",
145
- "--adland-badge-radius",
146
- "--adland-accent-bg",
147
- "--adland-accent-fg"
142
+ "--adland-media-border",
143
+ "--adland-label-size"
148
144
  ];
149
145
  function readThemeVars(host) {
150
146
  const styles = getComputedStyle(host);
@@ -176,6 +172,16 @@ function makeAdlandSlot(adapter) {
176
172
  "data-ad"
177
173
  ];
178
174
  shadowInited = false;
175
+ /**
176
+ * Whether the framed markup is wrapped in a real `<a>`.
177
+ *
178
+ * The click handler used to assume it always was, and told `handleClick`
179
+ * the URL had already been opened. That is only true when a destination
180
+ * existed — and for four of the six ad types it did not, so the click was
181
+ * tracked and then dropped on the floor. Recording what was actually
182
+ * rendered lets the adapter take over when the anchor is missing.
183
+ */
184
+ framedInAnchor = false;
179
185
  cleanupImpression;
180
186
  /**
181
187
  * The address this element is actually showing.
@@ -248,8 +254,10 @@ function makeAdlandSlot(adapter) {
248
254
  }
249
255
  if (token !== this.renderToken || !this.isConnected) return;
250
256
  const inner = result.data ? cardMarkup(result.data) : emptyMarkup();
251
- const mode = this.attr("render-mode") === "inline" ? "inline" : "isolated";
257
+ const requested = this.attr("render-mode");
258
+ const mode = requested === "inline" || requested === "light" ? requested : "isolated";
252
259
  if (mode === "inline") this.renderInline(inner, result.data);
260
+ else if (mode === "light") this.renderLight(inner, result.data);
253
261
  else this.renderIsolated(inner, result.data);
254
262
  const resolved = this.attr("slot") ?? result.slot;
255
263
  this.resolvedSlot = resolved;
@@ -274,9 +282,46 @@ function makeAdlandSlot(adapter) {
274
282
  const card = root.querySelector(".adland-card");
275
283
  card?.addEventListener("click", () => this.handleClick(data, false));
276
284
  }
285
+ /**
286
+ * Into the page itself — no iframe, no shadow root, no styles of ours.
287
+ *
288
+ * ── The mode that exists so you can use your own CSS ─────────────────────
289
+ *
290
+ * The other two isolate, and both isolate from the publisher as much as
291
+ * from the advertiser. `isolated` seals the card in an iframe, where the
292
+ * only way in is the `--adland-*` custom properties copied across the
293
+ * document boundary. `inline` uses a shadow root, which custom properties
294
+ * inherit through and `::part()` can reach — better, and still not a
295
+ * stylesheet: Tailwind emits classes, and a class in the page cannot cross
296
+ * a shadow boundary. So a publisher wanting `rounded-2xl` on the card had
297
+ * no way to say it.
298
+ *
299
+ * Here the markup is ordinary children of `<adland-slot>`. Every class hook
300
+ * — `.adland-card`, `.adland-media`, `.adland-title`, `.adland-description`
301
+ * — is a normal element in the page, so a page stylesheet, a Tailwind
302
+ * arbitrary variant, or a `cn()` on the host all apply directly.
303
+ *
304
+ * ── What you give up, and why it is not the default ──────────────────────
305
+ *
306
+ * Isolation runs both ways. In this mode the publisher's CSS can break the
307
+ * ad, and a reset of theirs can leave an advertiser's creative unreadable
308
+ * with nobody able to see it but their own visitors. `BASE_STYLES` is NOT
309
+ * injected — in the light DOM those rules would leak to the whole page —
310
+ * so an unstyled card is what arrives, and styling it is now the
311
+ * publisher's job rather than their option.
312
+ *
313
+ * That is a real cost and it is why this is opt-in and named. A publisher
314
+ * who wants it asks for it; everybody else keeps the sealed default.
315
+ */
316
+ renderLight(inner, data) {
317
+ this.innerHTML = inner;
318
+ const card = this.querySelector(".adland-card");
319
+ card?.addEventListener("click", () => this.handleClick(data, false));
320
+ }
277
321
  renderIsolated(inner, data) {
278
322
  this.replaceChildren();
279
323
  const url = data ? adWebUrl(data) : this.emptyUrl();
324
+ this.framedInAnchor = !!url;
280
325
  const body = url ? `<a href="${escapeAttr(url)}" target="_blank" rel="noopener noreferrer" style="display:block;text-decoration:none;color:inherit">${inner}</a>` : inner;
281
326
  const theme = readThemeVars(this);
282
327
  const iframe = document.createElement("iframe");
@@ -287,7 +332,7 @@ function makeAdlandSlot(adapter) {
287
332
  iframe.setAttribute("title", "Advertisement");
288
333
  iframe.setAttribute("scrolling", "no");
289
334
  iframe.style.cssText = "width:100%;border:0;display:block;height:100%";
290
- 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>`;
335
+ iframe.srcdoc = `<!doctype html><html><head><meta charset="utf-8"><style>:root{${theme}}html,body{margin:0;padding:0}body{padding:var(--adland-bleed,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>`;
291
336
  const handler = (e) => {
292
337
  if (e.source !== iframe.contentWindow) return;
293
338
  const msg = e.data;
@@ -295,7 +340,7 @@ function makeAdlandSlot(adapter) {
295
340
  iframe.style.height = `${msg.height}px`;
296
341
  this.style.minHeight = "";
297
342
  } else if (msg?.__adland === "click") {
298
- this.handleClick(data, true);
343
+ this.handleClick(data, this.framedInAnchor);
299
344
  }
300
345
  };
301
346
  this.messageHandler = handler;
@@ -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","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 \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\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, \"&quot;\").replace(/</g, \"&lt;\");\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"]}
1
+ {"version":3,"sources":["../src/render.ts","../src/theme.ts","../src/element.ts","../src/register.ts","../src/index.ts"],"names":["getAdIcon","getAdTitle","getAdDescription","adDestination","createReadClient","fetchAdByKey","fetchMetadataURI","fetchAdFromURI","trackVerifiedImpression","trackImpression","trackClick","WebActionAdapter"],"mappings":"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","file":"index.cjs","sourcesContent":["import { type AdData, adDestination } from \"@adland/data\";\nimport { getAdDescription, getAdIcon, getAdTitle } from \"@adland/react/core\";\n\n/**\n * Part names exposed for `::part()` styling in inline (shadow DOM) mode.\n *\n * `badge` was here and is gone. It rendered the ad's TYPE — \"Link\", \"Token\" —\n * which tells a reader nothing they want: they can see it is a link by looking\n * at it, and the word cost a chunk of a row that is already one line of title\n * and one of description. `label` stays, because \"Ad\" is a disclosure rather\n * than a description.\n */\nexport const AD_PARTS = [\n \"card\",\n \"media\",\n \"body\",\n \"title\",\n \"description\",\n \"label\",\n] as const;\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 \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n \"'\": \"&#39;\",\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, \"&quot;\").replace(/</g, \"&lt;\");\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 /* Resting shadow, not just a hover one. Only --adland-shadow-hover existed,\n which quietly ruled out every style whose card is raised at rest: a hard\n offset shadow is most of what makes a neo-brutalist card look like one, and\n there was no way to ask for it. Defaults to none, so nothing moves. */\n box-shadow: var(--adland-shadow, none);\n transition: var(\n --adland-transition,\n border-color 0.15s ease,\n box-shadow 0.15s ease,\n transform 0.15s ease\n );\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 /* The other half of a raised card: shrink the shadow AND move the card into\n the space it vacated, or the card appears to grow rather than press. No\n default, so a theme that sets neither is untouched. */\n transform: var(--adland-transform-hover, none);\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 border: var(--adland-media-border, none);\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-label {\n position: absolute;\n top: 6px;\n right: 8px;\n font-size: var(--adland-label-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 /*\n * The icon, not the image — this card is a compact row.\n *\n * `@adland/react` exposes both because an ad carries both, and which is right\n * depends on the shape of the space rather than on the ad: `getAdImage`\n * prefers a wide cover, `getAdIcon` prefers a square mark. `.adland-media` is\n * square by construction — `--adland-media-size` sets its width AND its\n * height — so it is unambiguously the second case.\n *\n * It was asking for the image, which for a link ad meant an 840x560 Open\n * Graph cover cropped into a 44px box: the middle of somebody's banner where\n * their favicon belongs, and nothing about it looking like a mistake rather\n * than a bad picture. Both getters fall back to the other, so an ad with only\n * one still renders.\n */\n const image = getAdIcon(ad);\n const title = getAdTitle(ad);\n const description = getAdDescription(ad);\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><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/**\n * Where a click on this ad should go, on the open web.\n *\n * Delegates to `adDestination` in `@adland/data`, which is what\n * `WebActionAdapter` in `@adland/react` has always used. This was a second,\n * shorter implementation that handled `link` and `miniapp` and returned null\n * for everything else — so in the default isolated mode a token, cast, tweet\n * or profile ad rendered no anchor and a click on it did nothing at all, while\n * the same ad in inline or light mode worked, because those route through the\n * adapter instead. One destination function, one behaviour, six ad types.\n */\nexport function adWebUrl(ad: AdData): string | null {\n return adDestination(ad);\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-bleed\",\n \"--adland-gap\",\n \"--adland-font\",\n \"--adland-shadow\",\n \"--adland-shadow-hover\",\n \"--adland-transform-hover\",\n \"--adland-transition\",\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-media-border\",\n \"--adland-label-size\",\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 fetchAdByKey,\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 /**\n * Whether the framed markup is wrapped in a real `<a>`.\n *\n * The click handler used to assume it always was, and told `handleClick`\n * the URL had already been opened. That is only true when a destination\n * existed — and for four of the six ad types it did not, so the click was\n * tracked and then dropped on the floor. Recording what was actually\n * rendered lets the adapter take over when the anchor is missing.\n */\n private framedInAnchor = 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 requested = this.attr(\"render-mode\");\n const mode =\n requested === \"inline\" || requested === \"light\"\n ? requested\n : \"isolated\";\n if (mode === \"inline\") this.renderInline(inner, result.data);\n else if (mode === \"light\") this.renderLight(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 /**\n * Into the page itself — no iframe, no shadow root, no styles of ours.\n *\n * ── The mode that exists so you can use your own CSS ─────────────────────\n *\n * The other two isolate, and both isolate from the publisher as much as\n * from the advertiser. `isolated` seals the card in an iframe, where the\n * only way in is the `--adland-*` custom properties copied across the\n * document boundary. `inline` uses a shadow root, which custom properties\n * inherit through and `::part()` can reach — better, and still not a\n * stylesheet: Tailwind emits classes, and a class in the page cannot cross\n * a shadow boundary. So a publisher wanting `rounded-2xl` on the card had\n * no way to say it.\n *\n * Here the markup is ordinary children of `<adland-slot>`. Every class hook\n * — `.adland-card`, `.adland-media`, `.adland-title`, `.adland-description`\n * — is a normal element in the page, so a page stylesheet, a Tailwind\n * arbitrary variant, or a `cn()` on the host all apply directly.\n *\n * ── What you give up, and why it is not the default ──────────────────────\n *\n * Isolation runs both ways. In this mode the publisher's CSS can break the\n * ad, and a reset of theirs can leave an advertiser's creative unreadable\n * with nobody able to see it but their own visitors. `BASE_STYLES` is NOT\n * injected — in the light DOM those rules would leak to the whole page —\n * so an unstyled card is what arrives, and styling it is now the\n * publisher's job rather than their option.\n *\n * That is a real cost and it is why this is opt-in and named. A publisher\n * who wants it asks for it; everybody else keeps the sealed default.\n */\n private renderLight(inner: string, data: AdData | null) {\n // `innerHTML` rather than a parsed fragment: `inner` is built by\n // `cardMarkup`, which escapes every interpolated value — `escapeHtml` for\n // text, `escapeAttr` for attributes — so the only markup here is ours.\n this.innerHTML = inner;\n const card = this.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 this.framedInAnchor = !!url;\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 /*\n * The frame is a viewport, and anything drawn outside the card's border\n * box is outside the viewport.\n *\n * On an ordinary page a shadow spills past its element and a `transform`\n * moves it over its neighbours; both are visible because the page has\n * room. Here the card is 100% of a body with no padding, so a\n * `--adland-shadow` of `6px 6px` and a `--adland-transform-hover` of\n * `translate(4px, 4px)` are simply cropped by the frame — which showed up\n * as the right border vanishing on hover, an effect with no cause\n * anywhere in the card's own CSS.\n *\n * `--adland-bleed` is the room to draw in. Zero by default, so nothing\n * moves for a theme that does not spill; a raised style sets it to at\n * least its shadow offset plus its hover travel. The height message below\n * reads `scrollHeight`, which includes body padding, so the frame still\n * sizes itself correctly.\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}body{padding:var(--adland-bleed,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 // Only skip the adapter when an anchor really did the navigating.\n // Passing `true` unconditionally meant an ad with no anchor tracked\n // the click and went nowhere.\n this.handleClick(data, this.framedInAnchor);\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.d.cts CHANGED
@@ -15,8 +15,16 @@ declare function makeAdlandSlot(adapter: ActionAdapter): CustomElementConstructo
15
15
  */
16
16
  declare function registerAdlandSlot(adapter: ActionAdapter, tag?: string): void;
17
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"];
18
+ /**
19
+ * Part names exposed for `::part()` styling in inline (shadow DOM) mode.
20
+ *
21
+ * `badge` was here and is gone. It rendered the ad's TYPE — "Link", "Token" —
22
+ * which tells a reader nothing they want: they can see it is a link by looking
23
+ * at it, and the word cost a chunk of a row that is already one line of title
24
+ * and one of description. `label` stays, because "Ad" is a disclosure rather
25
+ * than a description.
26
+ */
27
+ declare const AD_PARTS: readonly ["card", "media", "body", "title", "description", "label"];
20
28
 
21
29
  /**
22
30
  * The `--adland-*` custom properties the card reads. Custom properties inherit
@@ -24,6 +32,6 @@ declare const AD_PARTS: readonly ["card", "media", "body", "title", "description
24
32
  * iframe document boundary — so for isolated mode we read whichever of these
25
33
  * are set on the host and copy them into the iframe's `:root`.
26
34
  */
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"];
35
+ 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-bleed", "--adland-gap", "--adland-font", "--adland-shadow", "--adland-shadow-hover", "--adland-transform-hover", "--adland-transition", "--adland-media-size", "--adland-media-radius", "--adland-media-bg", "--adland-title-size", "--adland-title-weight", "--adland-description-size", "--adland-media-border", "--adland-label-size"];
28
36
 
29
37
  export { AD_PARTS, THEME_VARS, makeAdlandSlot, registerAdlandSlot };
package/dist/index.d.ts CHANGED
@@ -15,8 +15,16 @@ declare function makeAdlandSlot(adapter: ActionAdapter): CustomElementConstructo
15
15
  */
16
16
  declare function registerAdlandSlot(adapter: ActionAdapter, tag?: string): void;
17
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"];
18
+ /**
19
+ * Part names exposed for `::part()` styling in inline (shadow DOM) mode.
20
+ *
21
+ * `badge` was here and is gone. It rendered the ad's TYPE — "Link", "Token" —
22
+ * which tells a reader nothing they want: they can see it is a link by looking
23
+ * at it, and the word cost a chunk of a row that is already one line of title
24
+ * and one of description. `label` stays, because "Ad" is a disclosure rather
25
+ * than a description.
26
+ */
27
+ declare const AD_PARTS: readonly ["card", "media", "body", "title", "description", "label"];
20
28
 
21
29
  /**
22
30
  * The `--adland-*` custom properties the card reads. Custom properties inherit
@@ -24,6 +32,6 @@ declare const AD_PARTS: readonly ["card", "media", "body", "title", "description
24
32
  * iframe document boundary — so for isolated mode we read whichever of these
25
33
  * are set on the host and copy them into the iframe's `:root`.
26
34
  */
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"];
35
+ 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-bleed", "--adland-gap", "--adland-font", "--adland-shadow", "--adland-shadow-hover", "--adland-transform-hover", "--adland-transition", "--adland-media-size", "--adland-media-radius", "--adland-media-bg", "--adland-title-size", "--adland-title-weight", "--adland-description-size", "--adland-media-border", "--adland-label-size"];
28
36
 
29
37
  export { AD_PARTS, THEME_VARS, makeAdlandSlot, registerAdlandSlot };