@farcaster/snap-hono 1.4.3 → 1.4.4

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 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
- display: "flex",
383
- flexDirection: "row",
384
- alignItems: "flex-end",
385
- gap: 12,
386
- height: 100,
387
- width: "100%",
388
- }, ...barNodes);
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
- return 100;
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)
@@ -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",
3
+ "version": "1.4.4",
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.10.0"
31
+ "@farcaster/snap": "1.13.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
- display: "flex",
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", fontSize: 11, color: "#6B7280", marginBottom: 4 },
538
- String(bar.value),
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
- return 100;
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;
@@ -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
  }