@farcaster/snap 2.4.0 → 2.5.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.
Files changed (59) hide show
  1. package/dist/button-orientation-utils.d.ts +3 -0
  2. package/dist/button-orientation-utils.js +25 -0
  3. package/dist/constants.d.ts +1 -0
  4. package/dist/constants.js +1 -0
  5. package/dist/react/components/action-button.js +5 -5
  6. package/dist/react/components/cell-grid.js +12 -3
  7. package/dist/react/components/item-group.js +2 -1
  8. package/dist/react/components/item-layout-context.d.ts +2 -0
  9. package/dist/react/components/item-layout-context.js +7 -0
  10. package/dist/react/components/item.js +40 -3
  11. package/dist/react/components/stack.js +46 -37
  12. package/dist/react/components/toggle-group.js +6 -4
  13. package/dist/react-native/components/item-layout-context.d.ts +2 -0
  14. package/dist/react-native/components/item-layout-context.js +6 -0
  15. package/dist/react-native/components/snap-action-button.js +15 -2
  16. package/dist/react-native/components/snap-cell-grid.js +36 -5
  17. package/dist/react-native/components/snap-item-group.js +16 -6
  18. package/dist/react-native/components/snap-item.js +56 -13
  19. package/dist/react-native/components/snap-stack.js +32 -33
  20. package/dist/react-native/components/snap-toggle-group.js +5 -4
  21. package/dist/stack-horizontal-utils.d.ts +7 -5
  22. package/dist/stack-horizontal-utils.js +24 -14
  23. package/dist/ui/catalog.d.ts +70 -1
  24. package/dist/ui/catalog.js +4 -4
  25. package/dist/ui/cell-grid.d.ts +14 -0
  26. package/dist/ui/cell-grid.js +7 -7
  27. package/dist/ui/index.d.ts +2 -2
  28. package/dist/ui/index.js +1 -1
  29. package/dist/ui/item.d.ts +112 -1
  30. package/dist/ui/item.js +28 -2
  31. package/dist/ui/stack.d.ts +1 -0
  32. package/dist/ui/stack.js +3 -1
  33. package/dist/validator.js +19 -1
  34. package/llms.txt +3 -1
  35. package/package.json +1 -1
  36. package/src/button-orientation-utils.ts +36 -0
  37. package/src/constants.ts +1 -0
  38. package/src/react/components/action-button.tsx +5 -4
  39. package/src/react/components/cell-grid.tsx +13 -4
  40. package/src/react/components/item-group.tsx +19 -17
  41. package/src/react/components/item-layout-context.tsx +12 -0
  42. package/src/react/components/item.tsx +97 -4
  43. package/src/react/components/stack.tsx +51 -40
  44. package/src/react/components/toggle-group.tsx +6 -4
  45. package/src/react-native/components/item-layout-context.tsx +10 -0
  46. package/src/react-native/components/snap-action-button.tsx +15 -2
  47. package/src/react-native/components/snap-cell-grid.tsx +43 -6
  48. package/src/react-native/components/snap-item-group.tsx +31 -17
  49. package/src/react-native/components/snap-item.tsx +92 -14
  50. package/src/react-native/components/snap-stack.tsx +37 -36
  51. package/src/react-native/components/snap-toggle-group.tsx +5 -4
  52. package/src/stack-horizontal-utils.ts +32 -13
  53. package/src/ui/README.md +1 -1
  54. package/src/ui/catalog.ts +6 -5
  55. package/src/ui/cell-grid.ts +8 -7
  56. package/src/ui/index.ts +2 -2
  57. package/src/ui/item.ts +35 -5
  58. package/src/ui/stack.ts +3 -1
  59. package/src/validator.ts +29 -1
package/src/ui/index.ts CHANGED
@@ -16,8 +16,8 @@ export type { ToggleGroupProps } from "./toggle-group.js";
16
16
  export { inputProps } from "./input.js";
17
17
  export type { InputProps } from "./input.js";
18
18
 
19
- export { itemProps } from "./item.js";
20
- export type { ItemProps } from "./item.js";
19
+ export { itemProps, itemMediaProps } from "./item.js";
20
+ export type { ItemMediaProps, ItemProps } from "./item.js";
21
21
 
22
22
  export { itemGroupProps } from "./item-group.js";
23
23
  export type { ItemGroupProps } from "./item-group.js";
package/src/ui/item.ts CHANGED
@@ -1,13 +1,43 @@
1
1
  import { z } from "zod";
2
+ import { PROGRESS_COLOR_VALUES } from "../colors.js";
3
+ import { ICON_NAMES } from "./icon.js";
2
4
 
3
5
  export const ITEM_VARIANTS = ["default"] as const;
6
+ export const ITEM_MEDIA_VARIANTS = ["icon", "image"] as const;
4
7
  export const ITEM_MAX_TITLE_CHARS = 100;
5
8
  export const ITEM_MAX_DESCRIPTION_CHARS = 160;
9
+ export const ITEM_MAX_MEDIA_ALT_CHARS = 120;
6
10
 
7
- export const itemProps = z.object({
8
- title: z.string().min(1).max(ITEM_MAX_TITLE_CHARS),
9
- description: z.string().max(ITEM_MAX_DESCRIPTION_CHARS).optional(),
10
- variant: z.enum(ITEM_VARIANTS).optional(),
11
- });
11
+ const itemIconMediaProps = z
12
+ .object({
13
+ variant: z.literal("icon"),
14
+ name: z.enum(ICON_NAMES),
15
+ color: z.enum(PROGRESS_COLOR_VALUES).optional(),
16
+ })
17
+ .strict();
18
+
19
+ const itemImageMediaProps = z
20
+ .object({
21
+ variant: z.literal("image"),
22
+ url: z.string(),
23
+ alt: z.string().max(ITEM_MAX_MEDIA_ALT_CHARS).optional(),
24
+ round: z.boolean().optional(),
25
+ })
26
+ .strict();
27
+
28
+ export const itemMediaProps = z.discriminatedUnion("variant", [
29
+ itemIconMediaProps,
30
+ itemImageMediaProps,
31
+ ]);
32
+
33
+ export const itemProps = z
34
+ .object({
35
+ title: z.string().min(1).max(ITEM_MAX_TITLE_CHARS),
36
+ description: z.string().max(ITEM_MAX_DESCRIPTION_CHARS).optional(),
37
+ variant: z.enum(ITEM_VARIANTS).optional(),
38
+ media: itemMediaProps.optional(),
39
+ })
40
+ .strict();
12
41
 
13
42
  export type ItemProps = z.infer<typeof itemProps>;
43
+ export type ItemMediaProps = z.infer<typeof itemMediaProps>;
package/src/ui/stack.ts CHANGED
@@ -8,7 +8,9 @@ export const stackProps = z.object({
8
8
  direction: z.enum(STACK_DIRECTIONS).optional(),
9
9
  gap: z.enum(STACK_GAPS).optional(),
10
10
  justify: z.enum(STACK_JUSTIFY).optional(),
11
- /** Horizontal stacks only: fixed column grid (`2`–`6`). Prefer omitting this when children are stacks — they flex as row peers automatically. */
11
+ /** Horizontal stacks only: make direct children equal width. */
12
+ equalWidth: z.boolean().optional(),
13
+ /** Horizontal stacks only: legacy fixed equal-width column count (`2`–`6`). Prefer `equalWidth`. */
12
14
  columns: z.union([
13
15
  z.literal(2),
14
16
  z.literal(3),
package/src/validator.ts CHANGED
@@ -29,7 +29,12 @@ const URL_TARGET_ACTIONS = new Set([
29
29
  */
30
30
  function isLoopback(url: URL): boolean {
31
31
  const host = url.hostname;
32
- return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]";
32
+ return (
33
+ host === "localhost" ||
34
+ host === "127.0.0.1" ||
35
+ host === "::1" ||
36
+ host === "[::1]"
37
+ );
33
38
  }
34
39
 
35
40
  /**
@@ -84,6 +89,11 @@ type ElementShape = {
84
89
  on?: Record<string, { action?: string; params?: Record<string, unknown> }>;
85
90
  };
86
91
 
92
+ type ItemMediaShape = {
93
+ variant?: string;
94
+ url?: unknown;
95
+ };
96
+
87
97
  // ─── Structural validation ────────────────────────────
88
98
 
89
99
  /**
@@ -169,6 +179,24 @@ function validateUrls(elements: Record<string, unknown>): z.core.$ZodIssue[] {
169
179
  }
170
180
  }
171
181
 
182
+ if (
183
+ el.type === "item" &&
184
+ el.props?.media &&
185
+ typeof el.props.media === "object"
186
+ ) {
187
+ const media = el.props.media as ItemMediaShape;
188
+ if (media.variant === "image" && typeof media.url === "string") {
189
+ const error = validateUrl(media.url);
190
+ if (error) {
191
+ issues.push({
192
+ code: "custom",
193
+ message: error,
194
+ path: ["ui", "elements", id, "props", "media", "url"],
195
+ });
196
+ }
197
+ }
198
+ }
199
+
172
200
  // Validate action target URLs
173
201
  if (el.on) {
174
202
  for (const [event, binding] of Object.entries(el.on)) {