@farcaster/snap 1.5.2 → 2.0.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.
Files changed (153) hide show
  1. package/dist/constants.d.ts +0 -107
  2. package/dist/constants.js +0 -148
  3. package/dist/dataStore.d.ts +12 -0
  4. package/dist/dataStore.js +35 -0
  5. package/dist/index.d.ts +5 -3
  6. package/dist/index.js +4 -3
  7. package/dist/react/accent-context.d.ts +6 -0
  8. package/dist/react/accent-context.js +10 -0
  9. package/dist/react/catalog-renderer.d.ts +5 -0
  10. package/dist/react/catalog-renderer.js +37 -0
  11. package/dist/react/components/action-button.d.ts +6 -0
  12. package/dist/react/components/action-button.js +22 -0
  13. package/dist/react/components/badge.d.ts +5 -0
  14. package/dist/react/components/badge.js +18 -0
  15. package/dist/react/components/icon.d.ts +7 -0
  16. package/dist/react/components/icon.js +60 -0
  17. package/dist/react/components/image.d.ts +5 -0
  18. package/dist/react/components/image.js +15 -0
  19. package/dist/react/components/input.d.ts +5 -0
  20. package/dist/react/components/input.js +18 -0
  21. package/dist/react/components/item-group.d.ts +7 -0
  22. package/dist/react/components/item-group.js +17 -0
  23. package/dist/react/components/item.d.ts +7 -0
  24. package/dist/react/components/item.js +9 -0
  25. package/dist/react/components/progress.d.ts +5 -0
  26. package/dist/react/components/progress.js +11 -0
  27. package/dist/react/components/separator.d.ts +5 -0
  28. package/dist/react/components/separator.js +7 -0
  29. package/dist/react/components/slider.d.ts +5 -0
  30. package/dist/react/components/slider.js +21 -0
  31. package/dist/react/components/stack.d.ts +7 -0
  32. package/dist/react/components/stack.js +32 -0
  33. package/dist/react/components/switch.d.ts +5 -0
  34. package/dist/react/components/switch.js +23 -0
  35. package/dist/react/components/text.d.ts +5 -0
  36. package/dist/react/components/text.js +25 -0
  37. package/dist/react/components/toggle-group.d.ts +5 -0
  38. package/dist/react/components/toggle-group.js +52 -0
  39. package/dist/react/hooks/use-snap-accent.d.ts +13 -0
  40. package/dist/react/hooks/use-snap-accent.js +32 -0
  41. package/dist/react/index.d.ts +47 -0
  42. package/dist/react/index.js +191 -0
  43. package/dist/react/lib/preview-primary-css.d.ts +6 -0
  44. package/dist/react/lib/preview-primary-css.js +43 -0
  45. package/dist/react/lib/resolve-palette-hex.d.ts +2 -0
  46. package/dist/react/lib/resolve-palette-hex.js +10 -0
  47. package/dist/schemas.d.ts +14 -1629
  48. package/dist/schemas.js +14 -526
  49. package/dist/ui/badge.d.ts +52 -0
  50. package/dist/ui/badge.js +9 -0
  51. package/dist/ui/button.d.ts +42 -28
  52. package/dist/ui/button.js +7 -9
  53. package/dist/ui/catalog.d.ts +280 -155
  54. package/dist/ui/catalog.js +102 -83
  55. package/dist/ui/icon.d.ts +56 -0
  56. package/dist/ui/icon.js +51 -0
  57. package/dist/ui/image.d.ts +1 -0
  58. package/dist/ui/image.js +2 -2
  59. package/dist/ui/index.d.ts +20 -22
  60. package/dist/ui/index.js +10 -11
  61. package/dist/ui/input.d.ts +17 -0
  62. package/dist/ui/input.js +13 -0
  63. package/dist/ui/item-group.d.ts +12 -0
  64. package/dist/ui/item-group.js +7 -0
  65. package/dist/ui/item.d.ts +14 -0
  66. package/dist/ui/item.js +9 -0
  67. package/dist/ui/progress.d.ts +1 -11
  68. package/dist/ui/progress.js +21 -4
  69. package/dist/ui/schema.js +3 -3
  70. package/dist/ui/separator.d.ts +9 -0
  71. package/dist/ui/separator.js +5 -0
  72. package/dist/ui/slider.d.ts +4 -3
  73. package/dist/ui/slider.js +34 -5
  74. package/dist/ui/stack.d.ts +22 -1
  75. package/dist/ui/stack.js +8 -1
  76. package/dist/ui/switch.d.ts +8 -0
  77. package/dist/ui/switch.js +7 -0
  78. package/dist/ui/text.d.ts +15 -7
  79. package/dist/ui/text.js +8 -4
  80. package/dist/ui/toggle-group.d.ts +23 -0
  81. package/dist/ui/toggle-group.js +19 -0
  82. package/dist/validator.d.ts +5 -1
  83. package/dist/validator.js +6 -136
  84. package/package.json +72 -52
  85. package/src/constants.ts +0 -179
  86. package/src/dataStore.ts +62 -0
  87. package/src/index.ts +10 -20
  88. package/src/react/accent-context.tsx +29 -0
  89. package/src/react/catalog-renderer.tsx +39 -0
  90. package/src/react/components/action-button.tsx +48 -0
  91. package/src/react/components/badge.tsx +37 -0
  92. package/src/react/components/icon.tsx +115 -0
  93. package/src/react/components/image.tsx +33 -0
  94. package/src/react/components/input.tsx +36 -0
  95. package/src/react/components/item-group.tsx +43 -0
  96. package/src/react/components/item.tsx +33 -0
  97. package/src/react/components/progress.tsx +29 -0
  98. package/src/react/components/separator.tsx +14 -0
  99. package/src/react/components/slider.tsx +43 -0
  100. package/src/react/components/stack.tsx +55 -0
  101. package/src/react/components/switch.tsx +46 -0
  102. package/src/react/components/text.tsx +43 -0
  103. package/src/react/components/toggle-group.tsx +85 -0
  104. package/src/react/hooks/use-snap-accent.ts +45 -0
  105. package/src/react/index.tsx +321 -0
  106. package/src/react/lib/preview-primary-css.ts +57 -0
  107. package/src/react/lib/resolve-palette-hex.ts +20 -0
  108. package/src/schemas.ts +18 -644
  109. package/src/ui/badge.ts +13 -0
  110. package/src/ui/button.ts +9 -12
  111. package/src/ui/catalog.ts +106 -86
  112. package/src/ui/icon.ts +56 -0
  113. package/src/ui/image.ts +3 -2
  114. package/src/ui/index.ts +26 -29
  115. package/src/ui/input.ts +17 -0
  116. package/src/ui/item-group.ts +11 -0
  117. package/src/ui/item.ts +13 -0
  118. package/src/ui/progress.ts +25 -7
  119. package/src/ui/schema.ts +3 -3
  120. package/src/ui/separator.ts +9 -0
  121. package/src/ui/slider.ts +40 -10
  122. package/src/ui/stack.ts +9 -1
  123. package/src/ui/switch.ts +11 -0
  124. package/src/ui/text.ts +9 -4
  125. package/src/ui/toggle-group.ts +23 -0
  126. package/src/validator.ts +6 -176
  127. package/dist/ui/bar-chart.d.ts +0 -30
  128. package/dist/ui/bar-chart.js +0 -15
  129. package/dist/ui/button-group.d.ts +0 -19
  130. package/dist/ui/button-group.js +0 -18
  131. package/dist/ui/divider.d.ts +0 -3
  132. package/dist/ui/divider.js +0 -2
  133. package/dist/ui/grid.d.ts +0 -22
  134. package/dist/ui/grid.js +0 -16
  135. package/dist/ui/group.d.ts +0 -7
  136. package/dist/ui/group.js +0 -5
  137. package/dist/ui/list.d.ts +0 -13
  138. package/dist/ui/list.js +0 -13
  139. package/dist/ui/spacer.d.ts +0 -9
  140. package/dist/ui/spacer.js +0 -5
  141. package/dist/ui/text-input.d.ts +0 -7
  142. package/dist/ui/text-input.js +0 -12
  143. package/dist/ui/toggle.d.ts +0 -7
  144. package/dist/ui/toggle.js +0 -6
  145. package/src/ui/bar-chart.ts +0 -20
  146. package/src/ui/button-group.ts +0 -26
  147. package/src/ui/divider.ts +0 -5
  148. package/src/ui/grid.ts +0 -25
  149. package/src/ui/group.ts +0 -8
  150. package/src/ui/list.ts +0 -17
  151. package/src/ui/spacer.ts +0 -8
  152. package/src/ui/text-input.ts +0 -15
  153. package/src/ui/toggle.ts +0 -9
@@ -1,110 +1,3 @@
1
- export declare const POST_GRID_TAP_KEY: "grid_tap";
2
1
  export declare const SPEC_VERSION: "1.0";
3
2
  export declare const MEDIA_TYPE: "application/vnd.farcaster.snap+json";
4
- export declare const LIMITS: {
5
- readonly maxElementsPerPage: 5;
6
- readonly maxButtonsPerPage: 4;
7
- readonly maxTextInputChars: 280;
8
- readonly maxListItems: 4;
9
- readonly minListItems: 1;
10
- readonly minButtonGroupOptions: 2;
11
- readonly maxButtonGroupOptions: 4;
12
- readonly maxButtonGroupOptionChars: 40;
13
- readonly maxButtonLabelChars: 30;
14
- readonly listItemContentMaxChars: 100;
15
- readonly listItemTrailingMaxChars: 40;
16
- readonly minGridCols: 2;
17
- readonly maxGridCols: 64;
18
- readonly minGridRows: 2;
19
- readonly maxGridRows: 8;
20
- readonly minGroupChildren: 2;
21
- readonly maxGroupChildren: 3;
22
- readonly maxBarChartBars: 6;
23
- readonly barChartLabelMaxChars: 40;
24
- readonly maxEstimatedPageHeightPx: 500;
25
- };
26
- export declare const TEXT_STYLE: {
27
- readonly title: "title";
28
- readonly body: "body";
29
- readonly caption: "caption";
30
- readonly label: "label";
31
- };
32
- export declare const TEXT_STYLE_VALUES: readonly ["title", "body", "caption", "label"];
33
- export declare const TEXT_ALIGN_VALUES: readonly ["left", "center", "right"];
34
- export declare const IMAGE_ASPECT_VALUES: readonly ["1:1", "16:9", "4:3", "3:4", "9:16"];
35
- export declare const SPACER_SIZE: {
36
- readonly small: "small";
37
- readonly medium: "medium";
38
- readonly large: "large";
39
- };
40
- export declare const SPACER_SIZE_VALUES: readonly ["small", "medium", "large"];
41
- export declare const LIST_STYLE_VALUES: readonly ["ordered", "unordered", "plain"];
42
- export declare const DEFAULT_LIST_STYLE: "ordered";
43
- export declare const GRID_CELL_SIZE_VALUES: readonly ["auto", "square"];
44
- export declare const GRID_GAP_VALUES: readonly ["none", "small", "medium"];
45
- export declare const DEFAULT_GRID_GAP: "small";
46
- export declare const BUTTON_GROUP_STYLE: {
47
- readonly row: "row";
48
- readonly stack: "stack";
49
- readonly grid: "grid";
50
- };
51
- export declare const BUTTON_GROUP_STYLE_VALUES: readonly ["row", "stack", "grid"];
52
- export declare const BUTTON_ACTION: {
53
- readonly post: "post";
54
- readonly link: "link";
55
- readonly mini_app: "mini_app";
56
- readonly client: "client";
57
- };
58
- export declare const BUTTON_ACTION_VALUES: readonly ["post", "link", "mini_app", "client"];
59
- export declare const CLIENT_ACTION: {
60
- readonly view_cast: "view_cast";
61
- readonly view_profile: "view_profile";
62
- readonly compose_cast: "compose_cast";
63
- readonly view_token: "view_token";
64
- readonly send_token: "send_token";
65
- readonly swap_token: "swap_token";
66
- };
67
- export declare const CLIENT_ACTION_VALUES: readonly ["view_cast", "view_profile", "compose_cast", "view_token", "send_token", "swap_token"];
68
- export declare const BUTTON_STYLE: {
69
- readonly primary: "primary";
70
- readonly secondary: "secondary";
71
- };
72
- export declare const BUTTON_STYLE_VALUES: readonly ["primary", "secondary"];
73
- export declare const BUTTON_LAYOUT_VALUES: readonly ["stack", "row", "grid"];
74
- export declare const DEFAULT_BUTTON_LAYOUT: "stack";
75
3
  export declare const EFFECT_VALUES: readonly ["confetti"];
76
- export declare const GROUP_LAYOUT_VALUES: readonly ["row"];
77
- /** Only valid as `page.elements`: vertical container for the page body (matches json-render trees). */
78
- export declare const PAGE_ROOT_TYPE: {
79
- readonly stack: "stack";
80
- };
81
- export declare const ELEMENT_TYPE: {
82
- readonly text: "text";
83
- readonly image: "image";
84
- readonly divider: "divider";
85
- readonly spacer: "spacer";
86
- readonly progress: "progress";
87
- readonly list: "list";
88
- readonly grid: "grid";
89
- readonly text_input: "text_input";
90
- readonly slider: "slider";
91
- readonly button_group: "button_group";
92
- readonly toggle: "toggle";
93
- readonly group: "group";
94
- readonly bar_chart: "bar_chart";
95
- };
96
- export type ElementType = (typeof ELEMENT_TYPE)[keyof typeof ELEMENT_TYPE];
97
- export declare const HTTPS_PREFIX: "https://";
98
- export declare const HTTP_PREFIX: "http://";
99
- /** 6-digit hex only (#RRGGBB); used for grid cell backgrounds (free hex). */
100
- export declare const HEX_COLOR_6_RE: RegExp;
101
- export declare const TEXT_CONTENT_MAX: {
102
- readonly title: 80;
103
- readonly body: 160;
104
- readonly caption: 100;
105
- readonly label: 40;
106
- };
107
- export declare const SLIDER_STEP_ALIGN_EPS = 0.000001;
108
- export declare const DEFAULT_SLIDER_STEP: 1;
109
- export declare const MEDIA_ELEMENT_TYPES: ElementType[];
110
- export declare const INTERACTIVE_ELEMENT_TYPES: ElementType[];
package/dist/constants.js CHANGED
@@ -1,151 +1,3 @@
1
- export const POST_GRID_TAP_KEY = "grid_tap";
2
1
  export const SPEC_VERSION = "1.0";
3
2
  export const MEDIA_TYPE = "application/vnd.farcaster.snap+json";
4
- export const LIMITS = {
5
- maxElementsPerPage: 5,
6
- maxButtonsPerPage: 4,
7
- maxTextInputChars: 280,
8
- maxListItems: 4,
9
- minListItems: 1,
10
- minButtonGroupOptions: 2,
11
- maxButtonGroupOptions: 4,
12
- maxButtonGroupOptionChars: 40,
13
- maxButtonLabelChars: 30,
14
- listItemContentMaxChars: 100,
15
- listItemTrailingMaxChars: 40,
16
- minGridCols: 2,
17
- maxGridCols: 64,
18
- minGridRows: 2,
19
- maxGridRows: 8,
20
- minGroupChildren: 2,
21
- maxGroupChildren: 3,
22
- maxBarChartBars: 6,
23
- barChartLabelMaxChars: 40,
24
- maxEstimatedPageHeightPx: 500,
25
- };
26
- export const TEXT_STYLE = {
27
- title: "title",
28
- body: "body",
29
- caption: "caption",
30
- label: "label",
31
- };
32
- export const TEXT_STYLE_VALUES = [
33
- TEXT_STYLE.title,
34
- TEXT_STYLE.body,
35
- TEXT_STYLE.caption,
36
- TEXT_STYLE.label,
37
- ];
38
- export const TEXT_ALIGN_VALUES = ["left", "center", "right"];
39
- export const IMAGE_ASPECT_VALUES = [
40
- "1:1",
41
- "16:9",
42
- "4:3",
43
- "3:4",
44
- "9:16",
45
- ];
46
- export const SPACER_SIZE = {
47
- small: "small",
48
- medium: "medium",
49
- large: "large",
50
- };
51
- export const SPACER_SIZE_VALUES = [
52
- SPACER_SIZE.small,
53
- SPACER_SIZE.medium,
54
- SPACER_SIZE.large,
55
- ];
56
- export const LIST_STYLE_VALUES = ["ordered", "unordered", "plain"];
57
- export const DEFAULT_LIST_STYLE = "ordered";
58
- export const GRID_CELL_SIZE_VALUES = ["auto", "square"];
59
- export const GRID_GAP_VALUES = ["none", "small", "medium"];
60
- export const DEFAULT_GRID_GAP = "small";
61
- export const BUTTON_GROUP_STYLE = {
62
- row: "row",
63
- stack: "stack",
64
- grid: "grid",
65
- };
66
- export const BUTTON_GROUP_STYLE_VALUES = [
67
- BUTTON_GROUP_STYLE.row,
68
- BUTTON_GROUP_STYLE.stack,
69
- BUTTON_GROUP_STYLE.grid,
70
- ];
71
- export const BUTTON_ACTION = {
72
- post: "post",
73
- link: "link",
74
- mini_app: "mini_app",
75
- client: "client",
76
- };
77
- export const BUTTON_ACTION_VALUES = [
78
- BUTTON_ACTION.post,
79
- BUTTON_ACTION.link,
80
- BUTTON_ACTION.mini_app,
81
- BUTTON_ACTION.client,
82
- ];
83
- export const CLIENT_ACTION = {
84
- view_cast: "view_cast",
85
- view_profile: "view_profile",
86
- compose_cast: "compose_cast",
87
- view_token: "view_token",
88
- send_token: "send_token",
89
- swap_token: "swap_token",
90
- };
91
- export const CLIENT_ACTION_VALUES = [
92
- CLIENT_ACTION.view_cast,
93
- CLIENT_ACTION.view_profile,
94
- CLIENT_ACTION.compose_cast,
95
- CLIENT_ACTION.view_token,
96
- CLIENT_ACTION.send_token,
97
- CLIENT_ACTION.swap_token,
98
- ];
99
- export const BUTTON_STYLE = {
100
- primary: "primary",
101
- secondary: "secondary",
102
- };
103
- export const BUTTON_STYLE_VALUES = [
104
- BUTTON_STYLE.primary,
105
- BUTTON_STYLE.secondary,
106
- ];
107
- export const BUTTON_LAYOUT_VALUES = ["stack", "row", "grid"];
108
- export const DEFAULT_BUTTON_LAYOUT = BUTTON_LAYOUT_VALUES[0];
109
3
  export const EFFECT_VALUES = ["confetti"];
110
- export const GROUP_LAYOUT_VALUES = ["row"];
111
- /** Only valid as `page.elements`: vertical container for the page body (matches json-render trees). */
112
- export const PAGE_ROOT_TYPE = {
113
- stack: "stack",
114
- };
115
- export const ELEMENT_TYPE = {
116
- text: "text",
117
- image: "image",
118
- divider: "divider",
119
- spacer: "spacer",
120
- progress: "progress",
121
- list: "list",
122
- grid: "grid",
123
- text_input: "text_input",
124
- slider: "slider",
125
- button_group: "button_group",
126
- toggle: "toggle",
127
- group: "group",
128
- bar_chart: "bar_chart",
129
- };
130
- export const HTTPS_PREFIX = "https://";
131
- export const HTTP_PREFIX = "http://";
132
- /** 6-digit hex only (#RRGGBB); used for grid cell backgrounds (free hex). */
133
- export const HEX_COLOR_6_RE = /^#[0-9a-fA-F]{6}$/;
134
- export const TEXT_CONTENT_MAX = {
135
- [TEXT_STYLE.title]: 80,
136
- [TEXT_STYLE.body]: 160,
137
- [TEXT_STYLE.caption]: 100,
138
- [TEXT_STYLE.label]: 40,
139
- };
140
- export const SLIDER_STEP_ALIGN_EPS = 1e-6;
141
- export const DEFAULT_SLIDER_STEP = 1;
142
- export const MEDIA_ELEMENT_TYPES = [
143
- ELEMENT_TYPE.image,
144
- ELEMENT_TYPE.grid,
145
- ];
146
- export const INTERACTIVE_ELEMENT_TYPES = [
147
- ELEMENT_TYPE.button_group,
148
- ELEMENT_TYPE.slider,
149
- ELEMENT_TYPE.text_input,
150
- ELEMENT_TYPE.toggle,
151
- ];
@@ -0,0 +1,12 @@
1
+ export type DataStoreValue = string | number | boolean | null | DataStoreValue[] | {
2
+ [key: string]: DataStoreValue;
3
+ };
4
+ export type SnapDataStoreOperations = {
5
+ get(key: string): Promise<DataStoreValue | null>;
6
+ set(key: string, value: DataStoreValue): Promise<void>;
7
+ };
8
+ export type SnapDataStore = SnapDataStoreOperations & {
9
+ withLock<T>(fn: (store: SnapDataStoreOperations) => Promise<T>): Promise<T>;
10
+ };
11
+ export declare function createDefaultDataStore(): SnapDataStore;
12
+ export declare function createInMemoryDataStore(): SnapDataStore;
@@ -0,0 +1,35 @@
1
+ export function createDefaultDataStore() {
2
+ const err = new Error("Data store is not configured. Use withUpstash() from @farcaster/snap-upstash or provide a data store implementation.");
3
+ return {
4
+ get(_key) {
5
+ return Promise.reject(err);
6
+ },
7
+ set(_key, _value) {
8
+ return Promise.reject(err);
9
+ },
10
+ withLock(_fn) {
11
+ return Promise.reject(err);
12
+ },
13
+ };
14
+ }
15
+ export function createInMemoryDataStore() {
16
+ const data = new Map();
17
+ const ops = {
18
+ get: async (key) => {
19
+ return data.get(key) ?? null;
20
+ },
21
+ set: async (key, value) => {
22
+ data.set(key, value);
23
+ },
24
+ };
25
+ /** Serializes `withLock` callbacks so async work does not interleave across callers. */
26
+ let lockChain = Promise.resolve();
27
+ return {
28
+ ...ops,
29
+ withLock(fn) {
30
+ const run = lockChain.then(() => fn(ops));
31
+ lockChain = run.then(() => undefined, () => undefined);
32
+ return run;
33
+ },
34
+ };
35
+ }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- export { POST_GRID_TAP_KEY, PAGE_ROOT_TYPE, ELEMENT_TYPE, MEDIA_TYPE, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, CLIENT_ACTION, CLIENT_ACTION_VALUES, } from "./constants.js";
1
+ export type { Spec as SnapSpec, UIElement as SnapUIElement } from "@json-render/core";
2
+ export { SPEC_VERSION, MEDIA_TYPE, EFFECT_VALUES, } from "./constants.js";
2
3
  export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, type PaletteColor, } from "./colors.js";
3
- export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, firstPageResponseSchema, payloadSchema, clientActionSchema, createDefaultDataStore, type Button, type Element, type Elements, type GroupChildElement, type ClientAction, type SnapAction, type SnapPageElementInput, type SnapContext, type SnapResponse, type SnapHandlerResult, type SnapFunction, type SnapPayload, type DataStoreValue, type SnapDataStore, type SnapDataStoreOperations, } from "./schemas.js";
4
- export { validateSnapResponse, validateFirstPageResponse, type ValidationResult, } from "./validator.js";
4
+ export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, payloadSchema, type SnapAction, type SnapContext, type SnapResponse, type SnapHandlerResult, type SnapFunction, type SnapPayload, } from "./schemas.js";
5
+ export { validateSnapResponse, type ValidationResult, } from "./validator.js";
6
+ export { type DataStoreValue, type SnapDataStore, type SnapDataStoreOperations, createDefaultDataStore, createInMemoryDataStore, } from "./dataStore.js";
5
7
  export { type Middleware, useMiddleware } from "./middleware.js";
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
- export { POST_GRID_TAP_KEY, PAGE_ROOT_TYPE, ELEMENT_TYPE, MEDIA_TYPE, DEFAULT_LIST_STYLE, DEFAULT_SLIDER_STEP, CLIENT_ACTION, CLIENT_ACTION_VALUES, } from "./constants.js";
1
+ export { SPEC_VERSION, MEDIA_TYPE, EFFECT_VALUES, } from "./constants.js";
2
2
  export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, } from "./colors.js";
3
- export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, firstPageResponseSchema, payloadSchema, clientActionSchema, createDefaultDataStore, } from "./schemas.js";
4
- export { validateSnapResponse, validateFirstPageResponse, } from "./validator.js";
3
+ export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, payloadSchema, } from "./schemas.js";
4
+ export { validateSnapResponse, } from "./validator.js";
5
+ export { createDefaultDataStore, createInMemoryDataStore, } from "./dataStore.js";
5
6
  export { useMiddleware } from "./middleware.js";
@@ -0,0 +1,6 @@
1
+ import { type ReactNode } from "react";
2
+ export declare function SnapPreviewAccentProvider({ pageAccent, children, }: {
3
+ pageAccent: string | undefined;
4
+ children: ReactNode;
5
+ }): import("react/jsx-runtime").JSX.Element;
6
+ export declare function useSnapPreviewPageAccent(): string | undefined;
@@ -0,0 +1,10 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useContext } from "react";
4
+ const SnapPreviewAccentContext = createContext(null);
5
+ export function SnapPreviewAccentProvider({ pageAccent, children, }) {
6
+ return (_jsx(SnapPreviewAccentContext.Provider, { value: { pageAccent }, children: children }));
7
+ }
8
+ export function useSnapPreviewPageAccent() {
9
+ return useContext(SnapPreviewAccentContext)?.pageAccent;
10
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Maps snap json-render catalog types to React components.
3
+ * Keys match the snap wire-format `type` strings exactly.
4
+ */
5
+ export declare const SnapCatalogView: import("react").ComponentType<import("@json-render/react").CreateRendererProps>;
@@ -0,0 +1,37 @@
1
+ "use client";
2
+ import { createRenderer } from "@json-render/react";
3
+ import { snapJsonRenderCatalog } from "@farcaster/snap/ui";
4
+ import { SnapActionButton } from "./components/action-button.js";
5
+ import { SnapBadge } from "./components/badge.js";
6
+ import { SnapIcon } from "./components/icon.js";
7
+ import { SnapImage } from "./components/image.js";
8
+ import { SnapInput } from "./components/input.js";
9
+ import { SnapItem } from "./components/item.js";
10
+ import { SnapItemGroup } from "./components/item-group.js";
11
+ import { SnapProgress } from "./components/progress.js";
12
+ import { SnapSeparator } from "./components/separator.js";
13
+ import { SnapSlider } from "./components/slider.js";
14
+ import { SnapStack } from "./components/stack.js";
15
+ import { SnapSwitch } from "./components/switch.js";
16
+ import { SnapText } from "./components/text.js";
17
+ import { SnapToggleGroup } from "./components/toggle-group.js";
18
+ /**
19
+ * Maps snap json-render catalog types to React components.
20
+ * Keys match the snap wire-format `type` strings exactly.
21
+ */
22
+ export const SnapCatalogView = createRenderer(snapJsonRenderCatalog, {
23
+ badge: SnapBadge,
24
+ button: SnapActionButton,
25
+ icon: SnapIcon,
26
+ image: SnapImage,
27
+ input: SnapInput,
28
+ item: SnapItem,
29
+ item_group: SnapItemGroup,
30
+ progress: SnapProgress,
31
+ separator: SnapSeparator,
32
+ slider: SnapSlider,
33
+ stack: SnapStack,
34
+ switch: SnapSwitch,
35
+ text: SnapText,
36
+ toggle_group: SnapToggleGroup,
37
+ });
@@ -0,0 +1,6 @@
1
+ export declare function SnapActionButton({ element: { props }, emit, }: {
2
+ element: {
3
+ props: Record<string, unknown>;
4
+ };
5
+ emit: (name: string) => void;
6
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,22 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Button } from "@neynar/ui/button";
4
+ import { cn } from "@neynar/ui/utils";
5
+ import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
6
+ import { ICON_MAP } from "./icon.js";
7
+ const VARIANT_MAP = {
8
+ default: "default",
9
+ secondary: "secondary",
10
+ outline: "outline",
11
+ ghost: "ghost",
12
+ };
13
+ export function SnapActionButton({ element: { props }, emit, }) {
14
+ const label = String(props.label ?? "Action");
15
+ const variant = VARIANT_MAP[String(props.variant ?? "default")] ?? "default";
16
+ const iconName = props.icon ? String(props.icon) : undefined;
17
+ const accentStyle = useSnapAccentScopeStyle();
18
+ const Icon = iconName ? ICON_MAP[iconName] : undefined;
19
+ return (_jsx("div", { className: "w-full min-w-0 flex-1", style: accentStyle, children: _jsxs(Button, { type: "button", variant: variant, className: cn("w-full gap-2", variant === "default" &&
20
+ "hover:!bg-[var(--snap-action-primary-hover)]", variant !== "default" &&
21
+ "hover:!bg-[var(--snap-action-outline-hover)]"), onClick: () => emit("press"), children: [Icon && _jsx(Icon, { size: 16 }), label] }) }));
22
+ }
@@ -0,0 +1,5 @@
1
+ export declare function SnapBadge({ element: { props }, }: {
2
+ element: {
3
+ props: Record<string, unknown>;
4
+ };
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Badge } from "@neynar/ui/badge";
4
+ import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
5
+ import { ICON_MAP } from "./icon.js";
6
+ export function SnapBadge({ element: { props }, }) {
7
+ const content = String(props.label ?? "");
8
+ const color = props.color ? String(props.color) : undefined;
9
+ const iconName = props.icon ? String(props.icon) : undefined;
10
+ const accentStyle = useSnapAccentScopeStyle();
11
+ const isAccent = !color || color === "accent";
12
+ const Icon = iconName ? ICON_MAP[iconName] : undefined;
13
+ return (_jsx("span", { style: isAccent ? accentStyle : undefined, children: _jsxs(Badge, { variant: isAccent ? "default" : "outline", className: "gap-1",
14
+ // TODO: fix outline badge border color in @neynar/ui — too bright in dark mode
15
+ style: !isAccent
16
+ ? { borderColor: `var(--snap-color-${color})`, color: `var(--snap-color-${color})` }
17
+ : undefined, children: [Icon && _jsx(Icon, { size: 12 }), content] }) }));
18
+ }
@@ -0,0 +1,7 @@
1
+ import { type LucideIcon } from "lucide-react";
2
+ export declare const ICON_MAP: Record<string, LucideIcon>;
3
+ export declare function SnapIcon({ element: { props }, }: {
4
+ element: {
5
+ props: Record<string, unknown>;
6
+ };
7
+ }): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,60 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { ArrowRight, ArrowLeft, ExternalLink, ChevronRight, Check, X, AlertTriangle, Info, Clock, Heart, MessageCircle, Repeat, Share, User, Users, Star, Trophy, Zap, Flame, Gift, ImageIcon, Play, Pause, Wallet, Coins, Plus, Minus, RefreshCw, Bookmark, ThumbsUp, ThumbsDown, TrendingUp, TrendingDown, } from "lucide-react";
4
+ import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
5
+ export const ICON_MAP = {
6
+ "arrow-right": ArrowRight,
7
+ "arrow-left": ArrowLeft,
8
+ "external-link": ExternalLink,
9
+ "chevron-right": ChevronRight,
10
+ check: Check,
11
+ x: X,
12
+ "alert-triangle": AlertTriangle,
13
+ info: Info,
14
+ clock: Clock,
15
+ heart: Heart,
16
+ "message-circle": MessageCircle,
17
+ repeat: Repeat,
18
+ share: Share,
19
+ user: User,
20
+ users: Users,
21
+ star: Star,
22
+ trophy: Trophy,
23
+ zap: Zap,
24
+ flame: Flame,
25
+ gift: Gift,
26
+ image: ImageIcon,
27
+ play: Play,
28
+ pause: Pause,
29
+ wallet: Wallet,
30
+ coins: Coins,
31
+ plus: Plus,
32
+ minus: Minus,
33
+ "refresh-cw": RefreshCw,
34
+ bookmark: Bookmark,
35
+ "thumbs-up": ThumbsUp,
36
+ "thumbs-down": ThumbsDown,
37
+ "trending-up": TrendingUp,
38
+ "trending-down": TrendingDown,
39
+ };
40
+ const SIZE_PX = {
41
+ sm: 16,
42
+ md: 20,
43
+ };
44
+ export function SnapIcon({ element: { props }, }) {
45
+ const name = String(props.name ?? "info");
46
+ const size = SIZE_PX[String(props.size ?? "md")] ?? 20;
47
+ const color = props.color ? String(props.color) : undefined;
48
+ const accentStyle = useSnapAccentScopeStyle();
49
+ const Icon = ICON_MAP[name];
50
+ if (!Icon)
51
+ return null;
52
+ const isAccent = !color || color === "accent";
53
+ return (_jsx("span", { style: {
54
+ display: "inline-flex",
55
+ alignItems: "center",
56
+ ...(isAccent ? accentStyle : {}),
57
+ }, children: _jsx(Icon, { size: size, style: isAccent
58
+ ? { color: "var(--snap-accent, currentColor)" }
59
+ : { color: `var(--snap-color-${color}, currentColor)` } }) }));
60
+ }
@@ -0,0 +1,5 @@
1
+ export declare function SnapImage({ element: { props }, }: {
2
+ element: {
3
+ props: Record<string, unknown>;
4
+ };
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,15 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { AspectRatio } from "@neynar/ui/aspect-ratio";
4
+ function aspectToRatio(aspect) {
5
+ const [w, h] = aspect.split(":").map(Number);
6
+ if (!w || !h)
7
+ return 1;
8
+ return w / h;
9
+ }
10
+ export function SnapImage({ element: { props }, }) {
11
+ const url = String(props.url ?? "");
12
+ const alt = String(props.alt ?? "");
13
+ const ratio = aspectToRatio(String(props.aspect ?? "1:1"));
14
+ return (_jsx(AspectRatio, { ratio: ratio, className: "relative w-full flex-1 overflow-hidden rounded-lg", children: _jsx("img", { src: url, alt: alt, className: "absolute inset-0 size-full object-cover" }) }));
15
+ }
@@ -0,0 +1,5 @@
1
+ export declare function SnapInput({ element: { props }, }: {
2
+ element: {
3
+ props: Record<string, unknown>;
4
+ };
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,18 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useId } from "react";
4
+ import { useStateStore } from "@json-render/react";
5
+ import { Input } from "@neynar/ui/input";
6
+ import { Label } from "@neynar/ui/label";
7
+ export function SnapInput({ element: { props }, }) {
8
+ const id = useId();
9
+ const { get, set } = useStateStore();
10
+ const name = String(props.name ?? "input");
11
+ const path = `/inputs/${name}`;
12
+ const label = props.label ? String(props.label) : undefined;
13
+ const placeholder = props.placeholder ? String(props.placeholder) : undefined;
14
+ const maxLength = typeof props.maxLength === "number" ? props.maxLength : undefined;
15
+ const raw = get(path);
16
+ const value = typeof raw === "string" ? raw : "";
17
+ return (_jsxs("div", { className: "w-full space-y-1.5", children: [label && _jsx(Label, { htmlFor: id, children: label }), _jsx(Input, { id: id, value: value, onChange: (e) => set(path, e.target.value), placeholder: placeholder, maxLength: maxLength })] }));
18
+ }
@@ -0,0 +1,7 @@
1
+ import { type ReactNode } from "react";
2
+ export declare function SnapItemGroup({ element: { props }, children, }: {
3
+ element: {
4
+ props: Record<string, unknown>;
5
+ };
6
+ children?: ReactNode;
7
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,17 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Children, Fragment } from "react";
4
+ import { cn } from "@neynar/ui/utils";
5
+ const GAP_MAP = {
6
+ none: "gap-0",
7
+ sm: "gap-1",
8
+ md: "gap-2",
9
+ lg: "gap-3",
10
+ };
11
+ export function SnapItemGroup({ element: { props }, children, }) {
12
+ const border = Boolean(props.border);
13
+ const separator = Boolean(props.separator);
14
+ const gap = GAP_MAP[String(props.gap ?? "sm")] ?? "gap-1";
15
+ const items = Children.toArray(children);
16
+ return (_jsx("div", { className: cn("flex flex-col", border && "rounded-lg border", gap), children: items.map((child, i) => (_jsxs(Fragment, { children: [separator && i > 0 && (_jsx("div", { className: "h-px bg-border" })), child] }, i))) }));
17
+ }
@@ -0,0 +1,7 @@
1
+ import type { ReactNode } from "react";
2
+ export declare function SnapItem({ element: { props }, children, }: {
3
+ element: {
4
+ props: Record<string, unknown>;
5
+ };
6
+ children?: ReactNode;
7
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { Item, ItemContent, ItemTitle, ItemDescription, ItemActions, } from "@neynar/ui/item";
4
+ export function SnapItem({ element: { props }, children, }) {
5
+ const title = String(props.title ?? "");
6
+ const description = props.description ? String(props.description) : undefined;
7
+ const variant = props.variant ?? "default";
8
+ return (_jsxs(Item, { variant: variant, className: `flex-1 py-1.5 px-2.5 ${variant === "muted" ? "!bg-border/20" : ""}`, children: [_jsxs(ItemContent, { className: "gap-0.5", children: [_jsx(ItemTitle, { children: title }), description && _jsx(ItemDescription, { className: "mt-0", children: description })] }), children && _jsx(ItemActions, { children: children })] }));
9
+ }
@@ -0,0 +1,5 @@
1
+ export declare function SnapProgress({ element: { props }, }: {
2
+ element: {
3
+ props: Record<string, unknown>;
4
+ };
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,11 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
4
+ export function SnapProgress({ element: { props }, }) {
5
+ const accentStyle = useSnapAccentScopeStyle();
6
+ const value = Number(props.value ?? 0);
7
+ const max = Math.max(1, Number(props.max ?? 100));
8
+ const percent = Math.min(100, Math.max(0, (value / max) * 100));
9
+ const label = props.label ? String(props.label) : null;
10
+ return (_jsxs("div", { className: "flex w-full flex-1 flex-col gap-1", style: accentStyle, children: [label && (_jsx("span", { className: "text-muted-foreground text-xs", children: label })), _jsx("div", { className: "bg-muted h-2.5 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "h-full rounded-full bg-primary transition-all", style: { width: `${percent}%` } }) })] }));
11
+ }
@@ -0,0 +1,5 @@
1
+ export declare function SnapSeparator({ element: { props }, }: {
2
+ element: {
3
+ props: Record<string, unknown>;
4
+ };
5
+ }): import("react/jsx-runtime").JSX.Element;