@farcaster/snap-hono 1.2.0 → 1.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/fallback.d.ts +5 -0
- package/dist/fallback.js +66 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +104 -44
- package/dist/og-image.d.ts +46 -0
- package/dist/og-image.js +628 -0
- package/dist/renderSnapPage.d.ts +18 -1
- package/dist/renderSnapPage.js +98 -46
- package/package.json +4 -2
- package/src/fallback.ts +81 -0
- package/src/index.ts +139 -46
- package/src/og-image.ts +878 -0
- package/src/renderSnapPage.ts +221 -104
package/src/renderSnapPage.ts
CHANGED
|
@@ -1,16 +1,115 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
import type {
|
|
2
|
+
SnapPageElementInput,
|
|
3
|
+
SnapHandlerResult,
|
|
4
|
+
PaletteColor,
|
|
5
|
+
} from "@farcaster/snap";
|
|
6
|
+
import {
|
|
7
|
+
DEFAULT_THEME_ACCENT,
|
|
8
|
+
PALETTE_LIGHT_HEX,
|
|
9
|
+
PALETTE_COLOR_ACCENT,
|
|
10
|
+
} from "@farcaster/snap";
|
|
11
|
+
|
|
12
|
+
type SnapPage = SnapHandlerResult["page"];
|
|
13
|
+
type SnapPageButton = NonNullable<SnapPage["buttons"]>[number];
|
|
14
|
+
|
|
15
|
+
// ─── OG meta ────────────────────────────────────────────
|
|
16
|
+
|
|
17
|
+
export type RenderSnapPageOptions = {
|
|
18
|
+
/** Absolute URL of the /~/og-image PNG route. */
|
|
19
|
+
ogImageUrl?: string;
|
|
20
|
+
/** Canonical pathname + search of the snap page (e.g. "/snap" or "/"). */
|
|
21
|
+
resourcePath?: string;
|
|
22
|
+
/** Optional og:site_name value (e.g. from SNAP_OG_SITE_NAME env). */
|
|
23
|
+
siteName?: string;
|
|
12
24
|
};
|
|
13
25
|
|
|
26
|
+
type PageMeta = {
|
|
27
|
+
title: string;
|
|
28
|
+
description: string;
|
|
29
|
+
imageUrl?: string;
|
|
30
|
+
imageAlt?: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export function extractPageMeta(page: SnapPage): PageMeta {
|
|
34
|
+
let title = "Farcaster Snap";
|
|
35
|
+
let description = "";
|
|
36
|
+
let imageUrl: string | undefined;
|
|
37
|
+
let imageAlt: string | undefined;
|
|
38
|
+
|
|
39
|
+
for (const el of page.elements.children) {
|
|
40
|
+
if (el.type === "text") {
|
|
41
|
+
const style = el.style;
|
|
42
|
+
const content = el.content;
|
|
43
|
+
if (style === "title" && title === "Farcaster Snap" && content) {
|
|
44
|
+
title = content;
|
|
45
|
+
} else if (
|
|
46
|
+
(style === "body" || style === "caption") &&
|
|
47
|
+
!description &&
|
|
48
|
+
content
|
|
49
|
+
) {
|
|
50
|
+
description = content;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (el.type === "image" && !imageUrl) {
|
|
54
|
+
imageUrl = el.url;
|
|
55
|
+
imageAlt = el.alt;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
title,
|
|
61
|
+
description: description || title,
|
|
62
|
+
imageUrl,
|
|
63
|
+
imageAlt,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function buildOgMeta(opts: {
|
|
68
|
+
title: string;
|
|
69
|
+
description: string;
|
|
70
|
+
pageUrl: string;
|
|
71
|
+
ogImageUrl?: string;
|
|
72
|
+
imageAlt?: string;
|
|
73
|
+
siteName?: string;
|
|
74
|
+
}): string {
|
|
75
|
+
const { title, description, pageUrl, ogImageUrl, imageAlt, siteName } = opts;
|
|
76
|
+
|
|
77
|
+
const imgUrl = ogImageUrl ?? undefined;
|
|
78
|
+
const twitterCard = imgUrl ? "summary_large_image" : "summary";
|
|
79
|
+
|
|
80
|
+
const lines = [
|
|
81
|
+
`<meta name="description" content="${esc(description)}">`,
|
|
82
|
+
`<meta property="og:title" content="${esc(title)}">`,
|
|
83
|
+
`<meta property="og:description" content="${esc(description)}">`,
|
|
84
|
+
`<meta property="og:url" content="${esc(pageUrl)}">`,
|
|
85
|
+
`<meta property="og:type" content="website">`,
|
|
86
|
+
`<meta property="og:locale" content="en_US">`,
|
|
87
|
+
];
|
|
88
|
+
|
|
89
|
+
if (siteName) {
|
|
90
|
+
lines.push(`<meta property="og:site_name" content="${esc(siteName)}">`);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (imgUrl) {
|
|
94
|
+
lines.push(`<meta property="og:image" content="${esc(imgUrl)}">`);
|
|
95
|
+
lines.push(
|
|
96
|
+
`<meta property="og:image:alt" content="${esc(imageAlt ?? title)}">`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
lines.push(
|
|
101
|
+
`<meta name="twitter:card" content="${twitterCard}">`,
|
|
102
|
+
`<meta name="twitter:title" content="${esc(title)}">`,
|
|
103
|
+
`<meta name="twitter:description" content="${esc(description)}">`,
|
|
104
|
+
);
|
|
105
|
+
|
|
106
|
+
if (imgUrl) {
|
|
107
|
+
lines.push(`<meta name="twitter:image" content="${esc(imgUrl)}">`);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return lines.join("\n");
|
|
111
|
+
}
|
|
112
|
+
|
|
14
113
|
const FC_ICON = `<svg viewBox="0 0 520 457" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M519.801 0V61.6809H458.172V123.31H477.054V123.331H519.801V456.795H416.57L416.507 456.49L363.832 207.03C358.81 183.251 345.667 161.736 326.827 146.434C307.988 131.133 284.255 122.71 260.006 122.71H259.8C235.551 122.71 211.818 131.133 192.979 146.434C174.139 161.736 160.996 183.259 155.974 207.03L103.239 456.795H0V123.323H42.7471V123.31H61.6262V61.6809H0V0H519.801Z" fill="currentColor"/></svg>`;
|
|
15
114
|
|
|
16
115
|
function esc(s: string): string {
|
|
@@ -21,20 +120,24 @@ function esc(s: string): string {
|
|
|
21
120
|
.replace(/"/g, """);
|
|
22
121
|
}
|
|
23
122
|
|
|
24
|
-
function accentHex(accent:
|
|
25
|
-
return
|
|
123
|
+
function accentHex(accent: PaletteColor | undefined): string {
|
|
124
|
+
return accent && PALETTE_LIGHT_HEX[accent]
|
|
125
|
+
? PALETTE_LIGHT_HEX[accent]
|
|
126
|
+
: PALETTE_LIGHT_HEX[DEFAULT_THEME_ACCENT];
|
|
26
127
|
}
|
|
27
128
|
|
|
28
|
-
function colorHex(
|
|
29
|
-
|
|
30
|
-
|
|
129
|
+
function colorHex(
|
|
130
|
+
color: PaletteColor | typeof PALETTE_COLOR_ACCENT | undefined,
|
|
131
|
+
accent: string,
|
|
132
|
+
): string {
|
|
133
|
+
if (!color || color === PALETTE_COLOR_ACCENT) return accent;
|
|
134
|
+
return PALETTE_LIGHT_HEX[color] ?? accent;
|
|
31
135
|
}
|
|
32
136
|
|
|
33
137
|
// ─── Element renderers ──────────────────────────────────
|
|
34
138
|
|
|
35
|
-
function renderElement(el:
|
|
36
|
-
|
|
37
|
-
switch (type) {
|
|
139
|
+
function renderElement(el: SnapPageElementInput, accent: string): string {
|
|
140
|
+
switch (el.type) {
|
|
38
141
|
case "text":
|
|
39
142
|
return renderText(el, accent);
|
|
40
143
|
case "image":
|
|
@@ -59,25 +162,20 @@ function renderElement(el: Record<string, unknown>, accent: string): string {
|
|
|
59
162
|
return renderGroup(el, accent);
|
|
60
163
|
case "divider":
|
|
61
164
|
return `<hr style="border:none;border-top:1px solid #E5E7EB;margin:4px 0">`;
|
|
62
|
-
case "spacer":
|
|
63
|
-
|
|
64
|
-
small: "8px",
|
|
65
|
-
medium: "16px",
|
|
66
|
-
large: "24px",
|
|
67
|
-
};
|
|
68
|
-
return `<div style="height:${
|
|
69
|
-
sizes[(el.size as string) ?? "medium"] ?? "16px"
|
|
70
|
-
}"></div>`;
|
|
71
|
-
}
|
|
165
|
+
case "spacer":
|
|
166
|
+
return renderSpacer(el);
|
|
72
167
|
default:
|
|
73
168
|
return "";
|
|
74
169
|
}
|
|
75
170
|
}
|
|
76
171
|
|
|
77
|
-
function renderText(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
172
|
+
function renderText(
|
|
173
|
+
el: Extract<SnapPageElementInput, { type: "text" }>,
|
|
174
|
+
_accent: string,
|
|
175
|
+
): string {
|
|
176
|
+
const style = el.style;
|
|
177
|
+
const content = esc(el.content);
|
|
178
|
+
const align = el.align ?? "left";
|
|
81
179
|
const styles: Record<string, string> = {
|
|
82
180
|
title: "font-size:20px;font-weight:700;color:#111",
|
|
83
181
|
body: "font-size:15px;line-height:1.5;color:#374151",
|
|
@@ -90,27 +188,24 @@ function renderText(el: Record<string, unknown>, _accent: string): string {
|
|
|
90
188
|
};text-align:${align}">${content}</div>`;
|
|
91
189
|
}
|
|
92
190
|
|
|
93
|
-
function renderImage(
|
|
94
|
-
|
|
95
|
-
|
|
191
|
+
function renderImage(
|
|
192
|
+
el: Extract<SnapPageElementInput, { type: "image" }>,
|
|
193
|
+
): string {
|
|
194
|
+
const url = esc(el.url);
|
|
195
|
+
const aspect = el.aspect ?? "16:9";
|
|
96
196
|
const [w, h] = aspect.split(":").map(Number);
|
|
97
197
|
const ratio = w && h ? `${w}/${h}` : "16/9";
|
|
98
198
|
return `<div style="aspect-ratio:${ratio};border-radius:8px;overflow:hidden;background:#F3F4F6"><img src="${url}" alt="${esc(
|
|
99
|
-
|
|
199
|
+
el.alt ?? "",
|
|
100
200
|
)}" style="width:100%;height:100%;object-fit:cover"></div>`;
|
|
101
201
|
}
|
|
102
202
|
|
|
103
|
-
function renderGrid(
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const cells = el
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
color?: string;
|
|
110
|
-
content?: string;
|
|
111
|
-
}>;
|
|
112
|
-
const cellSize = (el.cellSize as string) ?? "auto";
|
|
113
|
-
const gap = (el.gap as string) ?? "small";
|
|
203
|
+
function renderGrid(
|
|
204
|
+
el: Extract<SnapPageElementInput, { type: "grid" }>,
|
|
205
|
+
): string {
|
|
206
|
+
const { cols, rows, cells } = el;
|
|
207
|
+
const cellSize = el.cellSize ?? "auto";
|
|
208
|
+
const gap = el.gap ?? "small";
|
|
114
209
|
const gapPx: Record<string, string> = {
|
|
115
210
|
none: "0",
|
|
116
211
|
small: "2px",
|
|
@@ -135,11 +230,12 @@ function renderGrid(el: Record<string, unknown>): string {
|
|
|
135
230
|
}">${cellsHtml}</div>`;
|
|
136
231
|
}
|
|
137
232
|
|
|
138
|
-
function renderProgress(
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const
|
|
233
|
+
function renderProgress(
|
|
234
|
+
el: Extract<SnapPageElementInput, { type: "progress" }>,
|
|
235
|
+
accent: string,
|
|
236
|
+
): string {
|
|
237
|
+
const { value, max, label } = el;
|
|
238
|
+
const color = colorHex(el.color, accent);
|
|
143
239
|
const pct = max > 0 ? Math.min(100, (value / max) * 100) : 0;
|
|
144
240
|
const labelHtml = label
|
|
145
241
|
? `<div style="font-size:13px;color:#6B7280;margin-bottom:4px">${esc(
|
|
@@ -149,19 +245,18 @@ function renderProgress(el: Record<string, unknown>, accent: string): string {
|
|
|
149
245
|
return `<div>${labelHtml}<div style="height:8px;background:#E5E7EB;border-radius:4px;overflow:hidden"><div style="height:100%;width:${pct}%;background:${color};border-radius:4px"></div></div></div>`;
|
|
150
246
|
}
|
|
151
247
|
|
|
152
|
-
function renderBarChart(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
}>;
|
|
248
|
+
function renderBarChart(
|
|
249
|
+
el: Extract<SnapPageElementInput, { type: "bar_chart" }>,
|
|
250
|
+
accent: string,
|
|
251
|
+
): string {
|
|
252
|
+
const { bars } = el;
|
|
158
253
|
const max =
|
|
159
|
-
|
|
160
|
-
const defaultColor = colorHex(el.color
|
|
254
|
+
el.max ?? Math.max(...bars.map((b: { value: number }) => b.value), 1);
|
|
255
|
+
const defaultColor = colorHex(el.color, accent);
|
|
161
256
|
|
|
162
257
|
let html = `<div style="display:flex;align-items:flex-end;gap:12px;height:120px">`;
|
|
163
258
|
for (const bar of bars) {
|
|
164
|
-
const color = bar.color
|
|
259
|
+
const color = colorHex(bar.color, defaultColor);
|
|
165
260
|
const pct = max > 0 ? (bar.value / max) * 100 : 0;
|
|
166
261
|
html += `<div style="flex:1;display:flex;flex-direction:column;align-items:center;height:100%;justify-content:flex-end">`;
|
|
167
262
|
html += `<div style="font-size:11px;color:#6B7280;margin-bottom:4px">${bar.value}</div>`;
|
|
@@ -175,12 +270,11 @@ function renderBarChart(el: Record<string, unknown>, accent: string): string {
|
|
|
175
270
|
return html;
|
|
176
271
|
}
|
|
177
272
|
|
|
178
|
-
function renderList(
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
}>;
|
|
273
|
+
function renderList(
|
|
274
|
+
el: Extract<SnapPageElementInput, { type: "list" }>,
|
|
275
|
+
): string {
|
|
276
|
+
const style = el.style ?? "ordered";
|
|
277
|
+
const { items } = el;
|
|
184
278
|
|
|
185
279
|
let html = "";
|
|
186
280
|
for (let i = 0; i < items.length; i++) {
|
|
@@ -204,11 +298,11 @@ function renderList(el: Record<string, unknown>): string {
|
|
|
204
298
|
}
|
|
205
299
|
|
|
206
300
|
function renderButtonGroup(
|
|
207
|
-
el:
|
|
301
|
+
el: Extract<SnapPageElementInput, { type: "button_group" }>,
|
|
208
302
|
accent: string,
|
|
209
303
|
): string {
|
|
210
|
-
const options = el
|
|
211
|
-
const layout =
|
|
304
|
+
const { options } = el;
|
|
305
|
+
const layout = el.style ?? "row";
|
|
212
306
|
const dir = layout === "stack" ? "column" : "row";
|
|
213
307
|
let html = `<div style="display:flex;flex-direction:${dir};gap:8px">`;
|
|
214
308
|
for (const opt of options) {
|
|
@@ -220,13 +314,12 @@ function renderButtonGroup(
|
|
|
220
314
|
return html;
|
|
221
315
|
}
|
|
222
316
|
|
|
223
|
-
function renderSlider(
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
const
|
|
228
|
-
const
|
|
229
|
-
const maxLabel = el.maxLabel as string | undefined;
|
|
317
|
+
function renderSlider(
|
|
318
|
+
el: Extract<SnapPageElementInput, { type: "slider" }>,
|
|
319
|
+
accent: string,
|
|
320
|
+
): string {
|
|
321
|
+
const { label, min, max, minLabel, maxLabel } = el;
|
|
322
|
+
const value = el.value ?? (min + max) / 2;
|
|
230
323
|
|
|
231
324
|
const labelHtml = label
|
|
232
325
|
? `<div style="font-size:13px;color:#6B7280;margin-bottom:4px">${esc(
|
|
@@ -243,14 +336,19 @@ function renderSlider(el: Record<string, unknown>, accent: string): string {
|
|
|
243
336
|
return `<div>${labelHtml}<div style="display:flex;align-items:center;gap:8px">${minL}<input type="range" min="${min}" max="${max}" value="${value}" disabled style="flex:1;accent-color:${accent};opacity:0.7">${maxL}</div></div>`;
|
|
244
337
|
}
|
|
245
338
|
|
|
246
|
-
function renderTextInput(
|
|
247
|
-
|
|
339
|
+
function renderTextInput(
|
|
340
|
+
el: Extract<SnapPageElementInput, { type: "text_input" }>,
|
|
341
|
+
): string {
|
|
342
|
+
const placeholder = esc(el.placeholder ?? "");
|
|
248
343
|
return `<input type="text" placeholder="${placeholder}" disabled style="width:100%;padding:10px 12px;border-radius:8px;border:1px solid #E5E7EB;background:#F9FAFB;font-size:14px;color:#9CA3AF;font-family:inherit;box-sizing:border-box">`;
|
|
249
344
|
}
|
|
250
345
|
|
|
251
|
-
function renderToggle(
|
|
252
|
-
|
|
253
|
-
|
|
346
|
+
function renderToggle(
|
|
347
|
+
el: Extract<SnapPageElementInput, { type: "toggle" }>,
|
|
348
|
+
accent: string,
|
|
349
|
+
): string {
|
|
350
|
+
const label = esc(el.label);
|
|
351
|
+
const { value } = el;
|
|
254
352
|
const bg = value ? accent : "#D1D5DB";
|
|
255
353
|
const tx = value ? "20px" : "2px";
|
|
256
354
|
return `<div style="display:flex;align-items:center;justify-content:space-between">
|
|
@@ -259,10 +357,23 @@ function renderToggle(el: Record<string, unknown>, accent: string): string {
|
|
|
259
357
|
</div>`;
|
|
260
358
|
}
|
|
261
359
|
|
|
262
|
-
function
|
|
263
|
-
|
|
360
|
+
function renderSpacer(
|
|
361
|
+
el: Extract<SnapPageElementInput, { type: "spacer" }>,
|
|
362
|
+
): string {
|
|
363
|
+
const sizes: Record<string, string> = {
|
|
364
|
+
small: "8px",
|
|
365
|
+
medium: "16px",
|
|
366
|
+
large: "24px",
|
|
367
|
+
};
|
|
368
|
+
return `<div style="height:${sizes[el.size ?? "medium"] ?? "16px"}"></div>`;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function renderGroup(
|
|
372
|
+
el: Extract<SnapPageElementInput, { type: "group" }>,
|
|
373
|
+
accent: string,
|
|
374
|
+
): string {
|
|
264
375
|
let html = `<div style="display:flex;gap:12px">`;
|
|
265
|
-
for (const child of children) {
|
|
376
|
+
for (const child of el.children) {
|
|
266
377
|
html += `<div style="flex:1">${renderElement(child, accent)}</div>`;
|
|
267
378
|
}
|
|
268
379
|
html += `</div>`;
|
|
@@ -272,8 +383,8 @@ function renderGroup(el: Record<string, unknown>, accent: string): string {
|
|
|
272
383
|
// ─── Buttons ────────────────────────────────────────────
|
|
273
384
|
|
|
274
385
|
function renderButtons(
|
|
275
|
-
buttons:
|
|
276
|
-
layout:
|
|
386
|
+
buttons: SnapPage["buttons"],
|
|
387
|
+
layout: SnapPage["button_layout"],
|
|
277
388
|
accent: string,
|
|
278
389
|
): string {
|
|
279
390
|
if (!buttons || buttons.length === 0) return "";
|
|
@@ -293,13 +404,15 @@ function renderButtons(
|
|
|
293
404
|
|
|
294
405
|
let html = `<div style="${wrap}${dir};gap:8px;margin-top:12px">`;
|
|
295
406
|
for (let i = 0; i < buttons.length; i++) {
|
|
296
|
-
const btn = buttons[i]!;
|
|
297
|
-
const label = esc(btn.label
|
|
298
|
-
const style =
|
|
407
|
+
const btn: SnapPageButton = buttons[i]!;
|
|
408
|
+
const label = esc(btn.label);
|
|
409
|
+
const style = btn.style ?? (i === 0 ? "primary" : "secondary");
|
|
299
410
|
const bg = style === "primary" ? accent : "transparent";
|
|
300
411
|
const color = style === "primary" ? "#fff" : accent;
|
|
301
412
|
const border = style === "primary" ? "none" : `2px solid ${accent}`;
|
|
302
|
-
|
|
413
|
+
const pad = style === "primary" ? "18px 16px" : "10px 16px";
|
|
414
|
+
const minH = style === "primary" ? "min-height:52px;" : "";
|
|
415
|
+
html += `<button onclick="showModal()" style="flex:1;${minH}padding:${pad};border-radius:10px;background:${bg};color:${color};border:${border};font-size:14px;font-weight:600;cursor:pointer;font-family:inherit;box-sizing:border-box">${label}</button>`;
|
|
303
416
|
}
|
|
304
417
|
html += `</div>`;
|
|
305
418
|
return html;
|
|
@@ -310,16 +423,23 @@ function renderButtons(
|
|
|
310
423
|
export function renderSnapPage(
|
|
311
424
|
snap: SnapHandlerResult,
|
|
312
425
|
snapOrigin: string,
|
|
426
|
+
opts?: RenderSnapPageOptions,
|
|
313
427
|
): string {
|
|
314
428
|
const page = snap.page;
|
|
315
429
|
const accent = accentHex(page.theme?.accent);
|
|
316
430
|
|
|
317
|
-
|
|
318
|
-
const
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
431
|
+
const meta = extractPageMeta(page);
|
|
432
|
+
const pageTitle = esc(meta.title);
|
|
433
|
+
const resourcePath = opts?.resourcePath ?? "/";
|
|
434
|
+
const pageUrl = snapOrigin.replace(/\/$/, "") + resourcePath;
|
|
435
|
+
const ogMeta = buildOgMeta({
|
|
436
|
+
title: meta.title,
|
|
437
|
+
description: meta.description,
|
|
438
|
+
pageUrl,
|
|
439
|
+
ogImageUrl: opts?.ogImageUrl,
|
|
440
|
+
imageAlt: meta.imageAlt ?? meta.imageUrl ? meta.title : undefined,
|
|
441
|
+
siteName: opts?.siteName,
|
|
442
|
+
});
|
|
323
443
|
|
|
324
444
|
const snapUrl = encodeURIComponent(snapOrigin + "/");
|
|
325
445
|
|
|
@@ -327,17 +447,13 @@ export function renderSnapPage(
|
|
|
327
447
|
let elementsHtml = "";
|
|
328
448
|
for (const el of page.elements.children) {
|
|
329
449
|
elementsHtml += `<div style="margin-bottom:12px">${renderElement(
|
|
330
|
-
el
|
|
450
|
+
el,
|
|
331
451
|
accent,
|
|
332
452
|
)}</div>`;
|
|
333
453
|
}
|
|
334
454
|
|
|
335
455
|
// Render buttons
|
|
336
|
-
const buttonsHtml = renderButtons(
|
|
337
|
-
page.buttons as Array<Record<string, unknown>> | undefined,
|
|
338
|
-
page.button_layout as string | undefined,
|
|
339
|
-
accent,
|
|
340
|
-
);
|
|
456
|
+
const buttonsHtml = renderButtons(page.buttons, page.button_layout, accent);
|
|
341
457
|
|
|
342
458
|
return `<!DOCTYPE html>
|
|
343
459
|
<html lang="en">
|
|
@@ -345,6 +461,7 @@ export function renderSnapPage(
|
|
|
345
461
|
<meta charset="utf-8">
|
|
346
462
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
347
463
|
<title>${pageTitle}</title>
|
|
464
|
+
${ogMeta}
|
|
348
465
|
<style>
|
|
349
466
|
*{margin:0;padding:0;box-sizing:border-box}
|
|
350
467
|
body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;background:#0A0A0A;min-height:100vh;display:flex;align-items:center;justify-content:center;flex-direction:column;padding:24px}
|