@farcaster/snap 1.5.2 → 1.7.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 (208) 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/react-native/catalog-renderer.d.ts +5 -0
  48. package/dist/react-native/catalog-renderer.js +36 -0
  49. package/dist/react-native/components/snap-action-button.d.ts +2 -0
  50. package/dist/react-native/components/snap-action-button.js +68 -0
  51. package/dist/react-native/components/snap-badge.d.ts +2 -0
  52. package/dist/react-native/components/snap-badge.js +38 -0
  53. package/dist/react-native/components/snap-icon.d.ts +5 -0
  54. package/dist/react-native/components/snap-icon.js +56 -0
  55. package/dist/react-native/components/snap-image.d.ts +2 -0
  56. package/dist/react-native/components/snap-image.js +24 -0
  57. package/dist/react-native/components/snap-input.d.ts +2 -0
  58. package/dist/react-native/components/snap-input.js +36 -0
  59. package/dist/react-native/components/snap-item-group.d.ts +5 -0
  60. package/dist/react-native/components/snap-item-group.js +23 -0
  61. package/dist/react-native/components/snap-item.d.ts +5 -0
  62. package/dist/react-native/components/snap-item.js +45 -0
  63. package/dist/react-native/components/snap-progress.d.ts +2 -0
  64. package/dist/react-native/components/snap-progress.js +26 -0
  65. package/dist/react-native/components/snap-separator.d.ts +2 -0
  66. package/dist/react-native/components/snap-separator.js +23 -0
  67. package/dist/react-native/components/snap-slider.d.ts +2 -0
  68. package/dist/react-native/components/snap-slider.js +42 -0
  69. package/dist/react-native/components/snap-stack.d.ts +5 -0
  70. package/dist/react-native/components/snap-stack.js +49 -0
  71. package/dist/react-native/components/snap-switch.d.ts +2 -0
  72. package/dist/react-native/components/snap-switch.js +30 -0
  73. package/dist/react-native/components/snap-text.d.ts +2 -0
  74. package/dist/react-native/components/snap-text.js +37 -0
  75. package/dist/react-native/components/snap-toggle-group.d.ts +2 -0
  76. package/dist/react-native/components/snap-toggle-group.js +100 -0
  77. package/dist/react-native/index.d.ts +52 -0
  78. package/dist/react-native/index.js +155 -0
  79. package/dist/react-native/theme.d.ts +21 -0
  80. package/dist/react-native/theme.js +37 -0
  81. package/dist/react-native/use-snap-palette.d.ts +13 -0
  82. package/dist/react-native/use-snap-palette.js +48 -0
  83. package/dist/schemas.d.ts +14 -1629
  84. package/dist/schemas.js +14 -526
  85. package/dist/ui/badge.d.ts +52 -0
  86. package/dist/ui/badge.js +9 -0
  87. package/dist/ui/button.d.ts +42 -28
  88. package/dist/ui/button.js +7 -9
  89. package/dist/ui/catalog.d.ts +281 -156
  90. package/dist/ui/catalog.js +102 -83
  91. package/dist/ui/icon.d.ts +56 -0
  92. package/dist/ui/icon.js +51 -0
  93. package/dist/ui/image.d.ts +1 -0
  94. package/dist/ui/image.js +2 -2
  95. package/dist/ui/index.d.ts +20 -22
  96. package/dist/ui/index.js +10 -11
  97. package/dist/ui/input.d.ts +17 -0
  98. package/dist/ui/input.js +13 -0
  99. package/dist/ui/item-group.d.ts +12 -0
  100. package/dist/ui/item-group.js +7 -0
  101. package/dist/ui/item.d.ts +14 -0
  102. package/dist/ui/item.js +9 -0
  103. package/dist/ui/progress.d.ts +1 -11
  104. package/dist/ui/progress.js +21 -4
  105. package/dist/ui/schema.d.ts +1 -1
  106. package/dist/ui/schema.js +3 -3
  107. package/dist/ui/separator.d.ts +9 -0
  108. package/dist/ui/separator.js +5 -0
  109. package/dist/ui/slider.d.ts +4 -3
  110. package/dist/ui/slider.js +34 -5
  111. package/dist/ui/stack.d.ts +22 -1
  112. package/dist/ui/stack.js +8 -1
  113. package/dist/ui/switch.d.ts +8 -0
  114. package/dist/ui/switch.js +7 -0
  115. package/dist/ui/text.d.ts +15 -7
  116. package/dist/ui/text.js +8 -4
  117. package/dist/ui/toggle-group.d.ts +23 -0
  118. package/dist/ui/toggle-group.js +19 -0
  119. package/dist/validator.d.ts +5 -1
  120. package/dist/validator.js +6 -136
  121. package/package.json +78 -53
  122. package/src/constants.ts +0 -179
  123. package/src/dataStore.ts +62 -0
  124. package/src/index.ts +10 -20
  125. package/src/react/accent-context.tsx +29 -0
  126. package/src/react/catalog-renderer.tsx +39 -0
  127. package/src/react/components/action-button.tsx +48 -0
  128. package/src/react/components/badge.tsx +37 -0
  129. package/src/react/components/icon.tsx +115 -0
  130. package/src/react/components/image.tsx +33 -0
  131. package/src/react/components/input.tsx +36 -0
  132. package/src/react/components/item-group.tsx +43 -0
  133. package/src/react/components/item.tsx +33 -0
  134. package/src/react/components/progress.tsx +29 -0
  135. package/src/react/components/separator.tsx +14 -0
  136. package/src/react/components/slider.tsx +43 -0
  137. package/src/react/components/stack.tsx +55 -0
  138. package/src/react/components/switch.tsx +46 -0
  139. package/src/react/components/text.tsx +43 -0
  140. package/src/react/components/toggle-group.tsx +85 -0
  141. package/src/react/hooks/use-snap-accent.ts +45 -0
  142. package/src/react/index.tsx +321 -0
  143. package/src/react/lib/preview-primary-css.ts +57 -0
  144. package/src/react/lib/resolve-palette-hex.ts +20 -0
  145. package/src/react-native/catalog-renderer.tsx +37 -0
  146. package/src/react-native/components/snap-action-button.tsx +92 -0
  147. package/src/react-native/components/snap-badge.tsx +57 -0
  148. package/src/react-native/components/snap-icon.tsx +102 -0
  149. package/src/react-native/components/snap-image.tsx +38 -0
  150. package/src/react-native/components/snap-input.tsx +57 -0
  151. package/src/react-native/components/snap-item-group.tsx +43 -0
  152. package/src/react-native/components/snap-item.tsx +70 -0
  153. package/src/react-native/components/snap-progress.tsx +40 -0
  154. package/src/react-native/components/snap-separator.tsx +32 -0
  155. package/src/react-native/components/snap-slider.tsx +82 -0
  156. package/src/react-native/components/snap-stack.tsx +66 -0
  157. package/src/react-native/components/snap-switch.tsx +45 -0
  158. package/src/react-native/components/snap-text.tsx +53 -0
  159. package/src/react-native/components/snap-toggle-group.tsx +128 -0
  160. package/src/react-native/index.tsx +267 -0
  161. package/src/react-native/theme.tsx +73 -0
  162. package/src/react-native/use-snap-palette.ts +64 -0
  163. package/src/schemas.ts +18 -644
  164. package/src/ui/badge.ts +13 -0
  165. package/src/ui/button.ts +9 -12
  166. package/src/ui/catalog.ts +106 -86
  167. package/src/ui/icon.ts +56 -0
  168. package/src/ui/image.ts +3 -2
  169. package/src/ui/index.ts +26 -29
  170. package/src/ui/input.ts +17 -0
  171. package/src/ui/item-group.ts +11 -0
  172. package/src/ui/item.ts +13 -0
  173. package/src/ui/progress.ts +25 -7
  174. package/src/ui/schema.ts +3 -3
  175. package/src/ui/separator.ts +9 -0
  176. package/src/ui/slider.ts +40 -10
  177. package/src/ui/stack.ts +9 -1
  178. package/src/ui/switch.ts +11 -0
  179. package/src/ui/text.ts +9 -4
  180. package/src/ui/toggle-group.ts +23 -0
  181. package/src/validator.ts +6 -176
  182. package/dist/ui/bar-chart.d.ts +0 -30
  183. package/dist/ui/bar-chart.js +0 -15
  184. package/dist/ui/button-group.d.ts +0 -19
  185. package/dist/ui/button-group.js +0 -18
  186. package/dist/ui/divider.d.ts +0 -3
  187. package/dist/ui/divider.js +0 -2
  188. package/dist/ui/grid.d.ts +0 -22
  189. package/dist/ui/grid.js +0 -16
  190. package/dist/ui/group.d.ts +0 -7
  191. package/dist/ui/group.js +0 -5
  192. package/dist/ui/list.d.ts +0 -13
  193. package/dist/ui/list.js +0 -13
  194. package/dist/ui/spacer.d.ts +0 -9
  195. package/dist/ui/spacer.js +0 -5
  196. package/dist/ui/text-input.d.ts +0 -7
  197. package/dist/ui/text-input.js +0 -12
  198. package/dist/ui/toggle.d.ts +0 -7
  199. package/dist/ui/toggle.js +0 -6
  200. package/src/ui/bar-chart.ts +0 -20
  201. package/src/ui/button-group.ts +0 -26
  202. package/src/ui/divider.ts +0 -5
  203. package/src/ui/grid.ts +0 -25
  204. package/src/ui/group.ts +0 -8
  205. package/src/ui/list.ts +0 -17
  206. package/src/ui/spacer.ts +0 -8
  207. package/src/ui/text-input.ts +0 -15
  208. package/src/ui/toggle.ts +0 -9
@@ -0,0 +1,100 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useStateStore } from "@json-render/react-native";
3
+ import { Pressable, StyleSheet, Text, View } from "react-native";
4
+ import { useSnapPalette } from "../use-snap-palette.js";
5
+ import { useSnapTheme } from "../theme.js";
6
+ export function SnapToggleGroup({ element: { props }, }) {
7
+ const { get, set } = useStateStore();
8
+ const { accentHex } = useSnapPalette();
9
+ const { colors } = useSnapTheme();
10
+ const name = String(props.name ?? "toggle_group");
11
+ const path = `/inputs/${name}`;
12
+ const label = props.label ? String(props.label) : undefined;
13
+ const isMultiple = Boolean(props.multiple);
14
+ const orientation = String(props.orientation ?? "horizontal");
15
+ const options = Array.isArray(props.options)
16
+ ? props.options
17
+ : [];
18
+ const raw = get(path);
19
+ const defaultValue = props.defaultValue;
20
+ const selected = (() => {
21
+ if (raw !== undefined && raw !== null) {
22
+ return isMultiple
23
+ ? Array.isArray(raw)
24
+ ? raw
25
+ : []
26
+ : typeof raw === "string"
27
+ ? [raw]
28
+ : [];
29
+ }
30
+ if (defaultValue !== undefined) {
31
+ return Array.isArray(defaultValue)
32
+ ? defaultValue
33
+ : [String(defaultValue)];
34
+ }
35
+ return [];
36
+ })();
37
+ const isVertical = orientation === "vertical";
38
+ const handlePress = (opt) => {
39
+ if (isMultiple) {
40
+ const next = selected.includes(opt)
41
+ ? selected.filter((s) => s !== opt)
42
+ : [...selected, opt];
43
+ set(path, next);
44
+ }
45
+ else {
46
+ if (opt && opt !== selected[0])
47
+ set(path, opt);
48
+ }
49
+ };
50
+ return (_jsxs(View, { style: styles.wrap, children: [label ? _jsx(Text, { style: [styles.label, { color: colors.text }], children: label }) : null, _jsx(View, { style: [
51
+ styles.group,
52
+ { backgroundColor: colors.border + "33" },
53
+ isVertical ? styles.groupVertical : styles.groupHorizontal,
54
+ ], children: options.map((opt, index) => {
55
+ const isSelected = selected.includes(opt);
56
+ return (_jsx(Pressable, { style: ({ pressed }) => [
57
+ styles.option,
58
+ isSelected && { backgroundColor: accentHex },
59
+ pressed && styles.pressed,
60
+ !isVertical && styles.optionHorizontal,
61
+ ], onPress: () => handlePress(opt), children: _jsx(Text, { style: [
62
+ styles.optionText,
63
+ { color: colors.text },
64
+ isSelected && styles.optionTextSelected,
65
+ ], children: opt }) }, index));
66
+ }) })] }));
67
+ }
68
+ const styles = StyleSheet.create({
69
+ wrap: { width: "100%", gap: 6 },
70
+ label: { fontSize: 13, fontWeight: "500" },
71
+ group: {
72
+ padding: 4,
73
+ borderRadius: 8,
74
+ gap: 4,
75
+ },
76
+ groupHorizontal: {
77
+ flexDirection: "row",
78
+ },
79
+ groupVertical: {
80
+ flexDirection: "column",
81
+ },
82
+ option: {
83
+ paddingVertical: 8,
84
+ paddingHorizontal: 12,
85
+ borderRadius: 6,
86
+ alignItems: "center",
87
+ justifyContent: "center",
88
+ },
89
+ optionHorizontal: {
90
+ flex: 1,
91
+ },
92
+ pressed: { opacity: 0.88 },
93
+ optionText: {
94
+ fontSize: 13,
95
+ fontWeight: "500",
96
+ },
97
+ optionTextSelected: {
98
+ color: "#fff",
99
+ },
100
+ });
@@ -0,0 +1,52 @@
1
+ import type { Spec } from "@json-render/core";
2
+ import { useSnapTheme, type SnapNativeColors } from "./theme.js";
3
+ import { hexToRgba } from "./use-snap-palette.js";
4
+ export type JsonValue = string | number | boolean | null | JsonValue[] | {
5
+ [key: string]: JsonValue;
6
+ };
7
+ export type SnapPage = {
8
+ version: string;
9
+ theme?: {
10
+ accent?: string;
11
+ };
12
+ effects?: string[];
13
+ ui: Spec;
14
+ };
15
+ export type SnapActionHandlers = {
16
+ submit: (target: string, inputs: Record<string, JsonValue>) => void;
17
+ open_url: (target: string) => void;
18
+ open_mini_app: (target: string) => void;
19
+ view_cast: (params: {
20
+ hash: string;
21
+ }) => void;
22
+ view_profile: (params: {
23
+ fid: number;
24
+ }) => void;
25
+ compose_cast: (params: {
26
+ text?: string;
27
+ channelKey?: string;
28
+ embeds?: string[];
29
+ }) => void;
30
+ view_token: (params: {
31
+ token: string;
32
+ }) => void;
33
+ send_token: (params: {
34
+ token: string;
35
+ amount?: string;
36
+ recipientFid?: number;
37
+ recipientAddress?: string;
38
+ }) => void;
39
+ swap_token: (params: {
40
+ sellToken?: string;
41
+ buyToken?: string;
42
+ }) => void;
43
+ };
44
+ export { useSnapTheme, hexToRgba };
45
+ export type { SnapNativeColors };
46
+ export declare function SnapView({ snap, handlers, loading, appearance, colors, }: {
47
+ snap: SnapPage;
48
+ handlers: SnapActionHandlers;
49
+ loading?: boolean;
50
+ appearance?: "light" | "dark";
51
+ colors?: Partial<SnapNativeColors>;
52
+ }): import("react").JSX.Element;
@@ -0,0 +1,155 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { snapJsonRenderCatalog } from "@farcaster/snap/ui";
3
+ import { SnapCatalogView } from "./catalog-renderer.js";
4
+ import { SnapThemeProvider, useSnapTheme } from "./theme.js";
5
+ import { hexToRgba } from "./use-snap-palette.js";
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
+ import { ActivityIndicator, StyleSheet, View } from "react-native";
8
+ import { DEFAULT_THEME_ACCENT, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, } from "@farcaster/snap";
9
+ // ─── Re-exports ───────────────────────────────────────
10
+ export { useSnapTheme, hexToRgba };
11
+ // ─── Internal helpers ─────────────────────────────────
12
+ function applyStatePaths(model, changes) {
13
+ const entries = Array.isArray(changes)
14
+ ? changes.map((c) => [c.path, c.value])
15
+ : Object.entries(changes);
16
+ for (const [path, value] of entries) {
17
+ const trimmed = path.startsWith("/") ? path : `/${path}`;
18
+ const parts = trimmed.split("/").filter(Boolean);
19
+ if (parts.length < 2)
20
+ continue;
21
+ const [top, ...rest] = parts;
22
+ if (top === "inputs") {
23
+ if (typeof model.inputs !== "object" || model.inputs === null) {
24
+ model.inputs = {};
25
+ }
26
+ const inputs = model.inputs;
27
+ if (rest.length === 1) {
28
+ inputs[rest[0]] = value;
29
+ }
30
+ continue;
31
+ }
32
+ if (top === "theme") {
33
+ if (typeof model.theme !== "object" || model.theme === null) {
34
+ model.theme = {};
35
+ }
36
+ const theme = model.theme;
37
+ if (rest.length === 1) {
38
+ theme[rest[0]] = value;
39
+ }
40
+ }
41
+ }
42
+ }
43
+ function resolveAccentHex(accent, appearance) {
44
+ const map = appearance === "dark" ? PALETTE_DARK_HEX : PALETTE_LIGHT_HEX;
45
+ const name = accent && Object.hasOwn(map, accent) ? accent : DEFAULT_THEME_ACCENT;
46
+ return map[name];
47
+ }
48
+ // ─── SnapView ─────────────────────────────────────────
49
+ function SnapViewInner({ snap, handlers, loading = false, }) {
50
+ const { mode } = useSnapTheme();
51
+ const spec = snap.ui;
52
+ const accentHex = resolveAccentHex(snap.theme?.accent, mode);
53
+ const initialState = useMemo(() => ({
54
+ ...(spec.state ?? {}),
55
+ inputs: { ...(spec.state?.inputs ?? {}) },
56
+ theme: {
57
+ ...(spec.state?.theme ?? {}),
58
+ ...(snap.theme ? { accent: snap.theme.accent } : {}),
59
+ },
60
+ }), [spec, snap.theme]);
61
+ const stateRef = useRef(initialState);
62
+ useEffect(() => {
63
+ stateRef.current = {
64
+ inputs: {
65
+ ...(initialState.inputs ?? {}),
66
+ },
67
+ theme: {
68
+ ...(initialState.theme ?? {}),
69
+ },
70
+ };
71
+ }, [initialState]);
72
+ useEffect(() => {
73
+ const result = snapJsonRenderCatalog.validate(spec);
74
+ if (!result.success) {
75
+ // eslint-disable-next-line no-console
76
+ console.warn("[SnapView] catalog validation issues:", result.error);
77
+ }
78
+ }, [spec]);
79
+ const [pageKey, setPageKey] = useState(0);
80
+ useEffect(() => {
81
+ setPageKey((k) => k + 1);
82
+ }, [spec]);
83
+ const handlersRef = useRef(handlers);
84
+ handlersRef.current = handlers;
85
+ const handleAction = useCallback((name, params) => {
86
+ const inputs = (stateRef.current.inputs ?? {});
87
+ const p = (params ?? {});
88
+ const h = handlersRef.current;
89
+ switch (name) {
90
+ case "submit":
91
+ h.submit(String(p.target ?? ""), inputs);
92
+ break;
93
+ case "open_url":
94
+ h.open_url(String(p.target ?? ""));
95
+ break;
96
+ case "open_mini_app":
97
+ h.open_mini_app(String(p.target ?? ""));
98
+ break;
99
+ case "view_cast":
100
+ h.view_cast({ hash: String(p.hash ?? "") });
101
+ break;
102
+ case "view_profile":
103
+ h.view_profile({ fid: Number(p.fid ?? 0) });
104
+ break;
105
+ case "compose_cast":
106
+ h.compose_cast({
107
+ text: p.text ? String(p.text) : undefined,
108
+ channelKey: p.channelKey ? String(p.channelKey) : undefined,
109
+ embeds: Array.isArray(p.embeds) ? p.embeds : undefined,
110
+ });
111
+ break;
112
+ case "view_token":
113
+ h.view_token({ token: String(p.token ?? "") });
114
+ break;
115
+ case "send_token":
116
+ h.send_token({
117
+ token: String(p.token ?? ""),
118
+ amount: p.amount ? String(p.amount) : undefined,
119
+ recipientFid: p.recipientFid ? Number(p.recipientFid) : undefined,
120
+ recipientAddress: p.recipientAddress ? String(p.recipientAddress) : undefined,
121
+ });
122
+ break;
123
+ case "swap_token":
124
+ h.swap_token({
125
+ sellToken: p.sellToken ? String(p.sellToken) : undefined,
126
+ buyToken: p.buyToken ? String(p.buyToken) : undefined,
127
+ });
128
+ break;
129
+ default:
130
+ break;
131
+ }
132
+ }, []);
133
+ return (_jsxs(View, { style: styles.container, children: [loading && (_jsx(View, { style: [
134
+ styles.overlay,
135
+ {
136
+ backgroundColor: mode === "dark" ? "rgba(0,0,0,0.6)" : "rgba(255,255,255,0.75)",
137
+ },
138
+ ], children: _jsx(ActivityIndicator, { size: "large", color: accentHex }) })), _jsx(SnapCatalogView, { spec: spec, state: initialState, loading: false, onStateChange: (changes) => {
139
+ applyStatePaths(stateRef.current, changes);
140
+ }, onAction: handleAction }, pageKey)] }));
141
+ }
142
+ export function SnapView({ snap, handlers, loading = false, appearance = "dark", colors, }) {
143
+ return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapViewInner, { snap: snap, handlers: handlers, loading: loading }) }));
144
+ }
145
+ const styles = StyleSheet.create({
146
+ container: {
147
+ width: "100%",
148
+ },
149
+ overlay: {
150
+ ...StyleSheet.absoluteFillObject,
151
+ alignItems: "center",
152
+ justifyContent: "center",
153
+ zIndex: 10,
154
+ },
155
+ });
@@ -0,0 +1,21 @@
1
+ import { type ReactNode } from "react";
2
+ export type SnapNativeColors = {
3
+ bg: string;
4
+ surface: string;
5
+ text: string;
6
+ textSecondary: string;
7
+ border: string;
8
+ inputBg: string;
9
+ muted: string;
10
+ };
11
+ interface SnapThemeValue {
12
+ mode: "light" | "dark";
13
+ colors: SnapNativeColors;
14
+ }
15
+ export declare function SnapThemeProvider({ appearance, colors, children, }: {
16
+ appearance: "light" | "dark";
17
+ colors?: Partial<SnapNativeColors>;
18
+ children: ReactNode;
19
+ }): import("react").JSX.Element;
20
+ export declare function useSnapTheme(): SnapThemeValue;
21
+ export {};
@@ -0,0 +1,37 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useContext, useMemo } from "react";
3
+ const DEFAULT_LIGHT = {
4
+ bg: "#dfe3e8",
5
+ surface: "#ffffff",
6
+ text: "#111111",
7
+ textSecondary: "#6b7280",
8
+ border: "#d1d5db",
9
+ inputBg: "#ffffff",
10
+ muted: "#f9fafb",
11
+ };
12
+ const DEFAULT_DARK = {
13
+ bg: "#111318",
14
+ surface: "#1a1d24",
15
+ text: "#fafafa",
16
+ textSecondary: "#a1a1aa",
17
+ border: "#374151",
18
+ inputBg: "#1a1d24",
19
+ muted: "#27272a",
20
+ };
21
+ const SnapThemeContext = createContext({
22
+ mode: "dark",
23
+ colors: DEFAULT_DARK,
24
+ });
25
+ export function SnapThemeProvider({ appearance, colors, children, }) {
26
+ const value = useMemo(() => {
27
+ const defaults = appearance === "dark" ? DEFAULT_DARK : DEFAULT_LIGHT;
28
+ return {
29
+ mode: appearance,
30
+ colors: colors ? { ...defaults, ...colors } : defaults,
31
+ };
32
+ }, [appearance, colors]);
33
+ return (_jsx(SnapThemeContext.Provider, { value: value, children: children }));
34
+ }
35
+ export function useSnapTheme() {
36
+ return useContext(SnapThemeContext);
37
+ }
@@ -0,0 +1,13 @@
1
+ export declare function useSnapPalette(): {
2
+ appearance: "light" | "dark";
3
+ accentName: "gray" | "blue" | "red" | "amber" | "green" | "teal" | "purple" | "pink";
4
+ accentHex: string;
5
+ hex: (semantic: string) => string;
6
+ };
7
+ /** `#RRGGBB` + alpha → `rgba(...)` for React Native styles. */
8
+ export declare function hexToRgba(hex: string, alpha: number): string;
9
+ export declare function useSnapPreviewChromePalette(themeAccent: string | undefined): {
10
+ appearance: "light" | "dark";
11
+ accentName: "gray" | "blue" | "red" | "amber" | "green" | "teal" | "purple" | "pink";
12
+ accentHex: string;
13
+ };
@@ -0,0 +1,48 @@
1
+ import { DEFAULT_THEME_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, } from "@farcaster/snap";
2
+ import { useStateStore } from "@json-render/react-native";
3
+ import { useSnapTheme } from "./theme.js";
4
+ function resolveHex(name, appearance) {
5
+ const map = appearance === "dark" ? PALETTE_DARK_HEX : PALETTE_LIGHT_HEX;
6
+ if (Object.hasOwn(map, name)) {
7
+ return map[name];
8
+ }
9
+ return map.purple;
10
+ }
11
+ function isPaletteColor(s) {
12
+ return PALETTE_COLOR_VALUES.includes(s);
13
+ }
14
+ function themeAccentFromStore(get) {
15
+ const raw = get("/theme/accent");
16
+ if (typeof raw === "string" && isPaletteColor(raw)) {
17
+ return raw;
18
+ }
19
+ return DEFAULT_THEME_ACCENT;
20
+ }
21
+ export function useSnapPalette() {
22
+ const { mode } = useSnapTheme();
23
+ const { get } = useStateStore();
24
+ const accentName = themeAccentFromStore(get);
25
+ const accentHex = resolveHex(accentName, mode);
26
+ const hex = (semantic) => semantic === "accent" ? accentHex : resolveHex(semantic, mode);
27
+ return { appearance: mode, accentName, accentHex, hex };
28
+ }
29
+ /** `#RRGGBB` + alpha → `rgba(...)` for React Native styles. */
30
+ export function hexToRgba(hex, alpha) {
31
+ const m = /^#([0-9a-fA-F]{6})$/.exec(hex.trim());
32
+ if (!m) {
33
+ return `rgba(0,0,0,${alpha})`;
34
+ }
35
+ const n = Number.parseInt(m[1], 16);
36
+ const r = (n >> 16) & 255;
37
+ const g = (n >> 8) & 255;
38
+ const b = n & 255;
39
+ return `rgba(${r},${g},${b},${alpha})`;
40
+ }
41
+ export function useSnapPreviewChromePalette(themeAccent) {
42
+ const { mode } = useSnapTheme();
43
+ const accentName = typeof themeAccent === "string" && isPaletteColor(themeAccent)
44
+ ? themeAccent
45
+ : DEFAULT_THEME_ACCENT;
46
+ const accentHex = resolveHex(accentName, mode);
47
+ return { appearance: mode, accentName, accentHex };
48
+ }