@farcaster/snap-hono 1.4.3 → 1.4.5
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/og-image.js +45 -26
- package/dist/renderSnapPage.js +41 -0
- package/package.json +2 -2
- package/src/og-image.ts +54 -34
- package/src/renderSnapPage.ts +41 -0
package/dist/og-image.js
CHANGED
|
@@ -361,31 +361,39 @@ function mapBarChart(el, accent) {
|
|
|
361
361
|
const color = bar.color !== undefined && bar.color !== ""
|
|
362
362
|
? colorHex(bar.color, accent)
|
|
363
363
|
: chartDefault;
|
|
364
|
-
const pct = maxVal > 0 ? (bar.value / maxVal) * 100 : 0;
|
|
365
|
-
return h("div", {
|
|
366
|
-
display: "flex",
|
|
367
|
-
flex: 1,
|
|
368
|
-
flexDirection: "column",
|
|
369
|
-
alignItems: "center",
|
|
370
|
-
height: "100%",
|
|
371
|
-
justifyContent: "flex-end",
|
|
372
|
-
}, h("div", { display: "flex", fontSize: 11, color: "#6B7280", marginBottom: 4 }, String(bar.value)), h("div", {
|
|
373
|
-
display: "flex",
|
|
374
|
-
width: "100%",
|
|
375
|
-
height: `${pct}%`,
|
|
376
|
-
backgroundColor: color,
|
|
377
|
-
borderRadius: "4px 4px 0 0",
|
|
378
|
-
minHeight: 4,
|
|
379
|
-
}), h("div", { display: "flex", fontSize: 11, color: "#9CA3AF", marginTop: 4 }, bar.label.slice(0, 12)));
|
|
364
|
+
const pct = maxVal > 0 ? Math.min(100, (bar.value / maxVal) * 100) : 0;
|
|
365
|
+
return h("div", { display: "flex", flexDirection: "row", alignItems: "center", gap: 8, width: OG_CARD_INNER_WIDTH_PX }, h("div", { display: "flex", width: 80, fontSize: 12, color: "#6B7280", justifyContent: "flex-end" }, bar.label.slice(0, 20)), h("div", { display: "flex", flex: 1, height: 10, backgroundColor: "#E5E7EB", borderRadius: 9999, overflow: "hidden" }, h("div", { display: "flex", height: 10, width: `${pct}%`, backgroundColor: color, borderRadius: 9999 })), h("div", { display: "flex", width: 32, fontSize: 12, color: "#6B7280" }, String(bar.value)));
|
|
380
366
|
});
|
|
381
|
-
return h("div", {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
367
|
+
return h("div", { display: "flex", flexDirection: "column", gap: 8, width: OG_CARD_INNER_WIDTH_PX }, ...barNodes);
|
|
368
|
+
}
|
|
369
|
+
function mapCellGrid(el, accent) {
|
|
370
|
+
const cols = Number(el.cols ?? 2);
|
|
371
|
+
const rows = Number(el.rows ?? 2);
|
|
372
|
+
const cells = Array.isArray(el.cells) ? el.cells : [];
|
|
373
|
+
const gap = String(el.gap ?? "sm");
|
|
374
|
+
const gapMap = { none: 0, sm: 1, md: 2, lg: 4 };
|
|
375
|
+
const gapPx = gapMap[gap] ?? 1;
|
|
376
|
+
const cellW = Math.floor((OG_CARD_INNER_WIDTH_PX - (cols - 1) * gapPx) / cols);
|
|
377
|
+
const cellMap = new Map();
|
|
378
|
+
for (const c of cells) {
|
|
379
|
+
cellMap.set(`${Number(c.row ?? 0)},${Number(c.col ?? 0)}`, { color: c.color, content: c.content });
|
|
380
|
+
}
|
|
381
|
+
const rowNodes = [];
|
|
382
|
+
for (let r = 0; r < rows; r++) {
|
|
383
|
+
const cellNodes = [];
|
|
384
|
+
for (let c = 0; c < cols; c++) {
|
|
385
|
+
const cell = cellMap.get(`${r},${c}`);
|
|
386
|
+
const bg = cell?.color ? colorHex(cell.color, accent) : "#F3F4F6";
|
|
387
|
+
cellNodes.push(h("div", {
|
|
388
|
+
display: "flex", alignItems: "center", justifyContent: "center",
|
|
389
|
+
width: cellW, height: cellW > 28 ? 28 : cellW, borderRadius: 4,
|
|
390
|
+
backgroundColor: bg, border: "1px solid #E5E7EB",
|
|
391
|
+
fontSize: 10, fontWeight: 600, color: "#374151",
|
|
392
|
+
}, cell?.content ?? ""));
|
|
393
|
+
}
|
|
394
|
+
rowNodes.push(h("div", { display: "flex", flexDirection: "row", gap: gapPx }, ...cellNodes));
|
|
395
|
+
}
|
|
396
|
+
return h("div", { display: "flex", flexDirection: "column", gap: gapPx, width: OG_CARD_INNER_WIDTH_PX }, ...rowNodes);
|
|
389
397
|
}
|
|
390
398
|
function mapElement(el, accent, imageMap) {
|
|
391
399
|
const type = el.type;
|
|
@@ -429,6 +437,8 @@ function mapElement(el, accent, imageMap) {
|
|
|
429
437
|
return mapList(el);
|
|
430
438
|
case "bar_chart":
|
|
431
439
|
return mapBarChart(el, accent);
|
|
440
|
+
case "cell_grid":
|
|
441
|
+
return mapCellGrid(el, accent);
|
|
432
442
|
case "group": {
|
|
433
443
|
const children = el.children ?? [];
|
|
434
444
|
const childNodes = children
|
|
@@ -574,8 +584,17 @@ function estimateElementHeight(el, imageMap) {
|
|
|
574
584
|
return estimateListHeight(el);
|
|
575
585
|
case "toggle_group":
|
|
576
586
|
return estimateButtonGroupHeight(el);
|
|
577
|
-
case "bar_chart":
|
|
578
|
-
|
|
587
|
+
case "bar_chart": {
|
|
588
|
+
const bars = Array.isArray(el.bars) ? el.bars : [];
|
|
589
|
+
return Math.max(1, bars.length) * 26;
|
|
590
|
+
}
|
|
591
|
+
case "cell_grid": {
|
|
592
|
+
const rows = Number(el.rows ?? 2);
|
|
593
|
+
const gap = String(el.gap ?? "sm");
|
|
594
|
+
const gapMap = { none: 0, sm: 1, md: 2, lg: 4 };
|
|
595
|
+
const gapPx = gapMap[gap] ?? 1;
|
|
596
|
+
return rows * 28 + (rows - 1) * gapPx;
|
|
597
|
+
}
|
|
579
598
|
case "group": {
|
|
580
599
|
const children = el.children ?? [];
|
|
581
600
|
if (children.length === 0)
|
package/dist/renderSnapPage.js
CHANGED
|
@@ -287,6 +287,47 @@ function renderElement(key, spec, accent) {
|
|
|
287
287
|
html += `</div>`;
|
|
288
288
|
return html;
|
|
289
289
|
}
|
|
290
|
+
case "bar_chart": {
|
|
291
|
+
const bars = Array.isArray(p.bars) ? p.bars : [];
|
|
292
|
+
const chartColor = colorHex(p.color, accent);
|
|
293
|
+
const maxVal = p.max != null ? Number(p.max) : Math.max(...bars.map((b) => Number(b.value ?? 0)), 1);
|
|
294
|
+
let html = `<div style="display:flex;flex-direction:column;gap:8px;width:100%">`;
|
|
295
|
+
for (const bar of bars) {
|
|
296
|
+
const value = Number(bar.value ?? 0);
|
|
297
|
+
const pct = maxVal > 0 ? Math.min(100, (value / maxVal) * 100) : 0;
|
|
298
|
+
const fill = bar.color ? colorHex(bar.color, accent) : chartColor;
|
|
299
|
+
html += `<div style="display:flex;align-items:center;gap:8px">`;
|
|
300
|
+
html += `<span style="width:80px;flex-shrink:0;text-align:right;font-size:12px;color:#6B7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(String(bar.label ?? ""))}</span>`;
|
|
301
|
+
html += `<div style="flex:1;height:10px;background:#E5E7EB;border-radius:9999px;overflow:hidden"><div style="height:100%;width:${pct}%;background:${fill};border-radius:9999px;transition:width 0.3s"></div></div>`;
|
|
302
|
+
html += `<span style="width:32px;flex-shrink:0;font-size:12px;color:#6B7280;font-variant-numeric:tabular-nums">${value}</span>`;
|
|
303
|
+
html += `</div>`;
|
|
304
|
+
}
|
|
305
|
+
html += `</div>`;
|
|
306
|
+
return html;
|
|
307
|
+
}
|
|
308
|
+
case "cell_grid": {
|
|
309
|
+
const cols = Number(p.cols ?? 2);
|
|
310
|
+
const rows = Number(p.rows ?? 2);
|
|
311
|
+
const cells = Array.isArray(p.cells) ? p.cells : [];
|
|
312
|
+
const gap = String(p.gap ?? "sm");
|
|
313
|
+
const gapMap = { none: 0, sm: 1, md: 2, lg: 4 };
|
|
314
|
+
const gapPx = gapMap[gap] ?? 1;
|
|
315
|
+
const cellMap = new Map();
|
|
316
|
+
for (const c of cells) {
|
|
317
|
+
cellMap.set(`${Number(c.row ?? 0)},${Number(c.col ?? 0)}`, { color: c.color, content: c.content });
|
|
318
|
+
}
|
|
319
|
+
let html = `<div style="display:grid;grid-template-columns:repeat(${cols},minmax(0,1fr));gap:${gapPx}px;width:100%">`;
|
|
320
|
+
for (let r = 0; r < rows; r++) {
|
|
321
|
+
for (let c = 0; c < cols; c++) {
|
|
322
|
+
const cell = cellMap.get(`${r},${c}`);
|
|
323
|
+
const bg = cell?.color ? colorHex(cell.color, accent) : "transparent";
|
|
324
|
+
const content = cell?.content ? esc(cell.content) : "";
|
|
325
|
+
html += `<div style="min-height:28px;border:1px solid #E5E7EB;border-radius:4px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;color:#374151;background:${bg}">${content}</div>`;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
html += `</div>`;
|
|
329
|
+
return html;
|
|
330
|
+
}
|
|
290
331
|
default:
|
|
291
332
|
return "";
|
|
292
333
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farcaster/snap-hono",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.5",
|
|
4
4
|
"description": "Hono integration for Farcaster Snap servers",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"@resvg/resvg-wasm": "^2.6.2",
|
|
30
30
|
"satori": "^0.10.0",
|
|
31
|
-
"@farcaster/snap": "1.
|
|
31
|
+
"@farcaster/snap": "1.14.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"hono": ">=4.0.0"
|
package/src/og-image.ts
CHANGED
|
@@ -521,51 +521,60 @@ function mapBarChart(el: El, accent: string): VNode {
|
|
|
521
521
|
bar.color !== undefined && bar.color !== ""
|
|
522
522
|
? colorHex(bar.color as string, accent)
|
|
523
523
|
: chartDefault;
|
|
524
|
-
const pct = maxVal > 0 ? (bar.value / maxVal) * 100 : 0;
|
|
524
|
+
const pct = maxVal > 0 ? Math.min(100, (bar.value / maxVal) * 100) : 0;
|
|
525
525
|
return h(
|
|
526
526
|
"div",
|
|
527
|
-
{
|
|
528
|
-
|
|
529
|
-
flex: 1,
|
|
530
|
-
flexDirection: "column",
|
|
531
|
-
alignItems: "center",
|
|
532
|
-
height: "100%",
|
|
533
|
-
justifyContent: "flex-end",
|
|
534
|
-
},
|
|
527
|
+
{ display: "flex", flexDirection: "row", alignItems: "center", gap: 8, width: OG_CARD_INNER_WIDTH_PX },
|
|
528
|
+
h("div", { display: "flex", width: 80, fontSize: 12, color: "#6B7280", justifyContent: "flex-end" }, bar.label.slice(0, 20)),
|
|
535
529
|
h(
|
|
536
530
|
"div",
|
|
537
|
-
{ display: "flex",
|
|
538
|
-
|
|
539
|
-
),
|
|
540
|
-
h("div", {
|
|
541
|
-
display: "flex",
|
|
542
|
-
width: "100%",
|
|
543
|
-
height: `${pct}%`,
|
|
544
|
-
backgroundColor: color,
|
|
545
|
-
borderRadius: "4px 4px 0 0",
|
|
546
|
-
minHeight: 4,
|
|
547
|
-
}),
|
|
548
|
-
h(
|
|
549
|
-
"div",
|
|
550
|
-
{ display: "flex", fontSize: 11, color: "#9CA3AF", marginTop: 4 },
|
|
551
|
-
bar.label.slice(0, 12),
|
|
531
|
+
{ display: "flex", flex: 1, height: 10, backgroundColor: "#E5E7EB", borderRadius: 9999, overflow: "hidden" },
|
|
532
|
+
h("div", { display: "flex", height: 10, width: `${pct}%`, backgroundColor: color, borderRadius: 9999 }),
|
|
552
533
|
),
|
|
534
|
+
h("div", { display: "flex", width: 32, fontSize: 12, color: "#6B7280" }, String(bar.value)),
|
|
553
535
|
);
|
|
554
536
|
});
|
|
555
537
|
return h(
|
|
556
538
|
"div",
|
|
557
|
-
{
|
|
558
|
-
display: "flex",
|
|
559
|
-
flexDirection: "row",
|
|
560
|
-
alignItems: "flex-end",
|
|
561
|
-
gap: 12,
|
|
562
|
-
height: 100,
|
|
563
|
-
width: "100%",
|
|
564
|
-
},
|
|
539
|
+
{ display: "flex", flexDirection: "column", gap: 8, width: OG_CARD_INNER_WIDTH_PX },
|
|
565
540
|
...barNodes,
|
|
566
541
|
);
|
|
567
542
|
}
|
|
568
543
|
|
|
544
|
+
function mapCellGrid(el: El, accent: string): VNode {
|
|
545
|
+
const cols = Number(el.cols ?? 2);
|
|
546
|
+
const rows = Number(el.rows ?? 2);
|
|
547
|
+
const cells = Array.isArray(el.cells) ? (el.cells as Array<{ row?: number; col?: number; color?: string; content?: string }>) : [];
|
|
548
|
+
const gap = String(el.gap ?? "sm");
|
|
549
|
+
const gapMap: Record<string, number> = { none: 0, sm: 1, md: 2, lg: 4 };
|
|
550
|
+
const gapPx = gapMap[gap] ?? 1;
|
|
551
|
+
const cellW = Math.floor((OG_CARD_INNER_WIDTH_PX - (cols - 1) * gapPx) / cols);
|
|
552
|
+
|
|
553
|
+
const cellMap = new Map<string, { color?: string; content?: string }>();
|
|
554
|
+
for (const c of cells) {
|
|
555
|
+
cellMap.set(`${Number(c.row ?? 0)},${Number(c.col ?? 0)}`, { color: c.color, content: c.content });
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
const rowNodes = [];
|
|
559
|
+
for (let r = 0; r < rows; r++) {
|
|
560
|
+
const cellNodes = [];
|
|
561
|
+
for (let c = 0; c < cols; c++) {
|
|
562
|
+
const cell = cellMap.get(`${r},${c}`);
|
|
563
|
+
const bg = cell?.color ? colorHex(cell.color, accent) : "#F3F4F6";
|
|
564
|
+
cellNodes.push(
|
|
565
|
+
h("div", {
|
|
566
|
+
display: "flex", alignItems: "center", justifyContent: "center",
|
|
567
|
+
width: cellW, height: cellW > 28 ? 28 : cellW, borderRadius: 4,
|
|
568
|
+
backgroundColor: bg, border: "1px solid #E5E7EB",
|
|
569
|
+
fontSize: 10, fontWeight: 600, color: "#374151",
|
|
570
|
+
}, cell?.content ?? ""),
|
|
571
|
+
);
|
|
572
|
+
}
|
|
573
|
+
rowNodes.push(h("div", { display: "flex", flexDirection: "row", gap: gapPx }, ...cellNodes));
|
|
574
|
+
}
|
|
575
|
+
return h("div", { display: "flex", flexDirection: "column", gap: gapPx, width: OG_CARD_INNER_WIDTH_PX }, ...rowNodes);
|
|
576
|
+
}
|
|
577
|
+
|
|
569
578
|
function mapElement(
|
|
570
579
|
el: El,
|
|
571
580
|
accent: string,
|
|
@@ -631,6 +640,8 @@ function mapElement(
|
|
|
631
640
|
return mapList(el);
|
|
632
641
|
case "bar_chart":
|
|
633
642
|
return mapBarChart(el, accent);
|
|
643
|
+
case "cell_grid":
|
|
644
|
+
return mapCellGrid(el, accent);
|
|
634
645
|
case "group": {
|
|
635
646
|
const children = (el.children as El[]) ?? [];
|
|
636
647
|
const childNodes = children
|
|
@@ -793,8 +804,17 @@ function estimateElementHeight(el: El, imageMap: Map<string, string>): number {
|
|
|
793
804
|
return estimateListHeight(el);
|
|
794
805
|
case "toggle_group":
|
|
795
806
|
return estimateButtonGroupHeight(el);
|
|
796
|
-
case "bar_chart":
|
|
797
|
-
|
|
807
|
+
case "bar_chart": {
|
|
808
|
+
const bars = Array.isArray(el.bars) ? el.bars : [];
|
|
809
|
+
return Math.max(1, bars.length) * 26;
|
|
810
|
+
}
|
|
811
|
+
case "cell_grid": {
|
|
812
|
+
const rows = Number(el.rows ?? 2);
|
|
813
|
+
const gap = String(el.gap ?? "sm");
|
|
814
|
+
const gapMap: Record<string, number> = { none: 0, sm: 1, md: 2, lg: 4 };
|
|
815
|
+
const gapPx = gapMap[gap] ?? 1;
|
|
816
|
+
return rows * 28 + (rows - 1) * gapPx;
|
|
817
|
+
}
|
|
798
818
|
case "group": {
|
|
799
819
|
const children = (el.children as El[]) ?? [];
|
|
800
820
|
if (children.length === 0) return 0;
|
package/src/renderSnapPage.ts
CHANGED
|
@@ -341,6 +341,47 @@ function renderElement(
|
|
|
341
341
|
html += `</div>`;
|
|
342
342
|
return html;
|
|
343
343
|
}
|
|
344
|
+
case "bar_chart": {
|
|
345
|
+
const bars = Array.isArray(p.bars) ? (p.bars as Array<{ label?: string; value?: number; color?: string }>) : [];
|
|
346
|
+
const chartColor = colorHex(p.color as string | undefined, accent);
|
|
347
|
+
const maxVal = p.max != null ? Number(p.max) : Math.max(...bars.map((b) => Number(b.value ?? 0)), 1);
|
|
348
|
+
let html = `<div style="display:flex;flex-direction:column;gap:8px;width:100%">`;
|
|
349
|
+
for (const bar of bars) {
|
|
350
|
+
const value = Number(bar.value ?? 0);
|
|
351
|
+
const pct = maxVal > 0 ? Math.min(100, (value / maxVal) * 100) : 0;
|
|
352
|
+
const fill = bar.color ? colorHex(bar.color, accent) : chartColor;
|
|
353
|
+
html += `<div style="display:flex;align-items:center;gap:8px">`;
|
|
354
|
+
html += `<span style="width:80px;flex-shrink:0;text-align:right;font-size:12px;color:#6B7280;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(String(bar.label ?? ""))}</span>`;
|
|
355
|
+
html += `<div style="flex:1;height:10px;background:#E5E7EB;border-radius:9999px;overflow:hidden"><div style="height:100%;width:${pct}%;background:${fill};border-radius:9999px;transition:width 0.3s"></div></div>`;
|
|
356
|
+
html += `<span style="width:32px;flex-shrink:0;font-size:12px;color:#6B7280;font-variant-numeric:tabular-nums">${value}</span>`;
|
|
357
|
+
html += `</div>`;
|
|
358
|
+
}
|
|
359
|
+
html += `</div>`;
|
|
360
|
+
return html;
|
|
361
|
+
}
|
|
362
|
+
case "cell_grid": {
|
|
363
|
+
const cols = Number(p.cols ?? 2);
|
|
364
|
+
const rows = Number(p.rows ?? 2);
|
|
365
|
+
const cells = Array.isArray(p.cells) ? (p.cells as Array<{ row?: number; col?: number; color?: string; content?: string }>) : [];
|
|
366
|
+
const gap = String(p.gap ?? "sm");
|
|
367
|
+
const gapMap: Record<string, number> = { none: 0, sm: 1, md: 2, lg: 4 };
|
|
368
|
+
const gapPx = gapMap[gap] ?? 1;
|
|
369
|
+
const cellMap = new Map<string, { color?: string; content?: string }>();
|
|
370
|
+
for (const c of cells) {
|
|
371
|
+
cellMap.set(`${Number(c.row ?? 0)},${Number(c.col ?? 0)}`, { color: c.color, content: c.content });
|
|
372
|
+
}
|
|
373
|
+
let html = `<div style="display:grid;grid-template-columns:repeat(${cols},minmax(0,1fr));gap:${gapPx}px;width:100%">`;
|
|
374
|
+
for (let r = 0; r < rows; r++) {
|
|
375
|
+
for (let c = 0; c < cols; c++) {
|
|
376
|
+
const cell = cellMap.get(`${r},${c}`);
|
|
377
|
+
const bg = cell?.color ? colorHex(cell.color, accent) : "transparent";
|
|
378
|
+
const content = cell?.content ? esc(cell.content) : "";
|
|
379
|
+
html += `<div style="min-height:28px;border:1px solid #E5E7EB;border-radius:4px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:600;color:#374151;background:${bg}">${content}</div>`;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
html += `</div>`;
|
|
383
|
+
return html;
|
|
384
|
+
}
|
|
344
385
|
default:
|
|
345
386
|
return "";
|
|
346
387
|
}
|