@farcaster/snap 1.16.3 → 1.17.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.
@@ -41,7 +41,7 @@ export type SnapActionHandlers = {
41
41
  buyToken?: string;
42
42
  }) => void;
43
43
  };
44
- export declare function SnapCard({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, }: {
44
+ export declare function SnapCard({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, actionError, }: {
45
45
  snap: SnapPage;
46
46
  handlers: SnapActionHandlers;
47
47
  loading?: boolean;
@@ -51,4 +51,6 @@ export declare function SnapCard({ snap, handlers, loading, appearance, maxWidth
51
51
  showOverflowWarning?: boolean;
52
52
  onValidationError?: (result: ValidationResult) => void;
53
53
  validationErrorFallback?: ReactNode;
54
+ /** Server-side action error message to display inline. */
55
+ actionError?: string | null;
54
56
  }): import("react/jsx-runtime").JSX.Element;
@@ -4,9 +4,9 @@ import { SPEC_VERSION_2 } from "../constants.js";
4
4
  import { SnapCardV1 } from "./v1/snap-view.js";
5
5
  import { SnapCardV2 } from "./v2/snap-view.js";
6
6
  // ─── SnapCard ────────────────────────────────────────
7
- export function SnapCard({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, }) {
7
+ export function SnapCard({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, }) {
8
8
  if (snap.version === SPEC_VERSION_2) {
9
- return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }));
9
+ return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError }));
10
10
  }
11
- return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth }));
11
+ return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, actionError: actionError }));
12
12
  }
@@ -5,10 +5,11 @@ export declare function SnapViewV1({ snap, handlers, loading, appearance, }: {
5
5
  loading?: boolean;
6
6
  appearance?: "light" | "dark";
7
7
  }): import("react/jsx-runtime").JSX.Element;
8
- export declare function SnapCardV1({ snap, handlers, loading, appearance, maxWidth, }: {
8
+ export declare function SnapCardV1({ snap, handlers, loading, appearance, maxWidth, actionError, }: {
9
9
  snap: SnapPage;
10
10
  handlers: SnapActionHandlers;
11
11
  loading?: boolean;
12
12
  appearance?: "light" | "dark";
13
13
  maxWidth?: number;
14
+ actionError?: string | null;
14
15
  }): import("react/jsx-runtime").JSX.Element;
@@ -1,9 +1,15 @@
1
1
  "use client";
2
- import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { SnapViewCore } from "../snap-view-core.js";
4
4
  export function SnapViewV1({ snap, handlers, loading = false, appearance = "dark", }) {
5
5
  return (_jsx(SnapViewCore, { snap: snap, handlers: handlers, loading: loading, appearance: appearance }));
6
6
  }
7
- export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, }) {
8
- return (_jsx("div", { style: { position: "relative", width: "100%", maxWidth }, children: _jsx(SnapViewV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance }) }));
7
+ export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, actionError, }) {
8
+ return (_jsxs("div", { style: { position: "relative", width: "100%", maxWidth }, children: [_jsx(SnapViewV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance }), actionError && (_jsx("div", { style: {
9
+ padding: "8px 12px",
10
+ fontSize: 13,
11
+ color: appearance === "dark"
12
+ ? "rgba(255,100,100,0.9)"
13
+ : "rgba(200,0,0,0.8)",
14
+ }, children: actionError }))] }));
9
15
  }
@@ -9,7 +9,7 @@ export declare function SnapViewV2({ snap, handlers, loading, appearance, onVali
9
9
  onValidationError?: (result: ValidationResult) => void;
10
10
  validationErrorFallback?: ReactNode;
11
11
  }): import("react/jsx-runtime").JSX.Element | null;
12
- export declare function SnapCardV2({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, }: {
12
+ export declare function SnapCardV2({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, actionError, }: {
13
13
  snap: SnapPage;
14
14
  handlers: SnapActionHandlers;
15
15
  loading?: boolean;
@@ -18,4 +18,5 @@ export declare function SnapCardV2({ snap, handlers, loading, appearance, maxWid
18
18
  showOverflowWarning?: boolean;
19
19
  onValidationError?: (result: ValidationResult) => void;
20
20
  validationErrorFallback?: ReactNode;
21
+ actionError?: string | null;
21
22
  }): import("react/jsx-runtime").JSX.Element;
@@ -42,35 +42,42 @@ export function SnapViewV2({ snap, handlers, loading = false, appearance = "dark
42
42
  return (_jsx(SnapViewCore, { snap: snap, handlers: handlers, loading: loading, appearance: appearance }));
43
43
  }
44
44
  // ─── SnapCardV2 ──────────────────────────────────────
45
- export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, }) {
45
+ export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, }) {
46
46
  const maxHeight = showOverflowWarning ? SNAP_WARNING_HEIGHT : SNAP_MAX_HEIGHT;
47
47
  const bg = appearance === "dark" ? "rgba(0,0,0,0.85)" : "rgba(255,255,255,0.9)";
48
- return (_jsxs("div", { style: {
49
- position: "relative",
50
- width: "100%",
51
- maxWidth,
52
- maxHeight,
53
- overflow: "hidden",
54
- }, children: [_jsx(SnapViewV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }), showOverflowWarning && (_jsxs("div", { style: {
55
- position: "absolute",
56
- top: SNAP_MAX_HEIGHT,
57
- left: 0,
58
- right: 0,
59
- bottom: 0,
60
- pointerEvents: "none",
61
- zIndex: 10,
62
- }, children: [_jsx("div", { style: { borderTop: "1px dashed rgba(255,100,100,0.6)", position: "relative" }, children: _jsxs("span", { style: {
63
- position: "absolute",
64
- top: -10,
65
- right: 0,
66
- fontSize: 10,
67
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
68
- color: "rgba(255,100,100,0.7)",
69
- background: bg,
70
- padding: "1px 4px",
71
- borderRadius: 3,
72
- }, children: [SNAP_MAX_HEIGHT, "px"] }) }), _jsx("div", { style: {
73
- height: "100%",
74
- background: "repeating-linear-gradient(-45deg, transparent, transparent 8px, rgba(255,100,100,0.06) 8px, rgba(255,100,100,0.06) 16px)",
75
- } })] }))] }));
48
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
49
+ position: "relative",
50
+ width: "100%",
51
+ maxWidth,
52
+ maxHeight,
53
+ overflow: "hidden",
54
+ }, children: [_jsx(SnapViewV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }), showOverflowWarning && (_jsxs("div", { style: {
55
+ position: "absolute",
56
+ top: SNAP_MAX_HEIGHT,
57
+ left: 0,
58
+ right: 0,
59
+ bottom: 0,
60
+ pointerEvents: "none",
61
+ zIndex: 10,
62
+ }, children: [_jsx("div", { style: { borderTop: "1px dashed rgba(255,100,100,0.6)", position: "relative" }, children: _jsxs("span", { style: {
63
+ position: "absolute",
64
+ top: -10,
65
+ right: 0,
66
+ fontSize: 10,
67
+ fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
68
+ color: "rgba(255,100,100,0.7)",
69
+ background: bg,
70
+ padding: "1px 4px",
71
+ borderRadius: 3,
72
+ }, children: [SNAP_MAX_HEIGHT, "px"] }) }), _jsx("div", { style: {
73
+ height: "100%",
74
+ background: "repeating-linear-gradient(-45deg, transparent, transparent 8px, rgba(255,100,100,0.06) 8px, rgba(255,100,100,0.06) 16px)",
75
+ } })] }))] }), actionError && (_jsx("div", { style: {
76
+ maxWidth,
77
+ padding: "8px 12px",
78
+ fontSize: 13,
79
+ color: appearance === "dark"
80
+ ? "rgba(255,100,100,0.9)"
81
+ : "rgba(200,0,0,0.8)",
82
+ }, children: actionError }))] }));
76
83
  }
@@ -7,7 +7,7 @@ import { hexToRgba } from "./use-snap-palette.js";
7
7
  export type { JsonValue, SnapPage, SnapActionHandlers } from "./types.js";
8
8
  export { useSnapTheme, hexToRgba };
9
9
  export type { SnapNativeColors };
10
- export declare function SnapCard({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, }: {
10
+ export declare function SnapCard({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, }: {
11
11
  snap: SnapPage;
12
12
  handlers: SnapActionHandlers;
13
13
  loading?: boolean;
@@ -21,4 +21,6 @@ export declare function SnapCard({ snap, handlers, loading, appearance, colors,
21
21
  onValidationError?: (result: ValidationResult) => void;
22
22
  /** Custom fallback rendered when validation fails (v2 only). */
23
23
  validationErrorFallback?: ReactNode;
24
+ /** Server-side action error message to display inline. */
25
+ actionError?: string | null;
24
26
  }): import("react").JSX.Element;
@@ -7,9 +7,9 @@ import { SnapCardV2 } from "./v2/snap-view.js";
7
7
  // ─── Re-exports ───────────────────────────────────────
8
8
  export { useSnapTheme, hexToRgba };
9
9
  // ─── SnapCard (version-switching) ─────────────────────
10
- export function SnapCard({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, }) {
10
+ export function SnapCard({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, }) {
11
11
  if (snap.version === SPEC_VERSION_2) {
12
- return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }));
12
+ return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError }));
13
13
  }
14
- return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius }));
14
+ return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, actionError: actionError }));
15
15
  }
@@ -12,11 +12,12 @@ export declare function SnapViewV1({ snap, handlers, loading, appearance, colors
12
12
  appearance?: "light" | "dark";
13
13
  colors?: Partial<SnapNativeColors>;
14
14
  }): import("react").JSX.Element;
15
- export declare function SnapCardV1({ snap, handlers, loading, appearance, colors, borderRadius, }: {
15
+ export declare function SnapCardV1({ snap, handlers, loading, appearance, colors, borderRadius, actionError, }: {
16
16
  snap: SnapPage;
17
17
  handlers: SnapActionHandlers;
18
18
  loading?: boolean;
19
19
  appearance?: "light" | "dark";
20
20
  colors?: Partial<SnapNativeColors>;
21
21
  borderRadius?: number;
22
+ actionError?: string | null;
22
23
  }): import("react").JSX.Element;
@@ -1,5 +1,5 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
2
- import { View, StyleSheet } from "react-native";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { View, Text, StyleSheet } from "react-native";
3
3
  import { SnapThemeProvider, useSnapTheme } from "../theme.js";
4
4
  import { SnapViewCoreInner } from "../snap-view-core.js";
5
5
  // ─── SnapViewV1 (no validation, no height limits) ────
@@ -10,22 +10,30 @@ export function SnapViewV1({ snap, handlers, loading = false, appearance = "dark
10
10
  return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapViewV1Inner, { snap: snap, handlers: handlers, loading: loading }) }));
11
11
  }
12
12
  // ─── SnapCardV1 (card frame, no height limits) ───────
13
- function SnapCardV1Inner({ snap, handlers, loading = false, borderRadius, }) {
13
+ function SnapCardV1Inner({ snap, handlers, loading = false, borderRadius, actionError, appearance, }) {
14
14
  const { colors } = useSnapTheme();
15
- return (_jsx(View, { style: cardStyles.frameRing, children: _jsx(View, { style: [
16
- cardStyles.card,
17
- {
18
- borderRadius,
19
- borderColor: colors.border,
20
- backgroundColor: colors.surface,
21
- },
22
- ], children: _jsx(View, { style: cardStyles.body, children: _jsx(SnapViewV1Inner, { snap: snap, handlers: handlers, loading: loading }) }) }) }));
15
+ return (_jsxs(_Fragment, { children: [_jsx(View, { style: cardStyles.frameRing, children: _jsx(View, { style: [
16
+ cardStyles.card,
17
+ {
18
+ borderRadius,
19
+ borderColor: colors.border,
20
+ backgroundColor: colors.surface,
21
+ },
22
+ ], children: _jsx(View, { style: cardStyles.body, children: _jsx(SnapViewV1Inner, { snap: snap, handlers: handlers, loading: loading }) }) }) }), actionError && (_jsx(Text, { style: [
23
+ cardStyles.actionError,
24
+ {
25
+ color: appearance === "dark"
26
+ ? "rgba(255,100,100,0.9)"
27
+ : "rgba(200,0,0,0.8)",
28
+ },
29
+ ], children: actionError }))] }));
23
30
  }
24
- export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, }) {
25
- return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapCardV1Inner, { snap: snap, handlers: handlers, loading: loading, borderRadius: borderRadius }) }));
31
+ export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, actionError, }) {
32
+ return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapCardV1Inner, { snap: snap, handlers: handlers, loading: loading, borderRadius: borderRadius, actionError: actionError, appearance: appearance }) }));
26
33
  }
27
34
  const cardStyles = StyleSheet.create({
28
35
  frameRing: { alignSelf: "stretch" },
29
36
  card: { overflow: "hidden", borderWidth: 1, minHeight: 120 },
30
37
  body: { paddingHorizontal: 16, paddingVertical: 16 },
38
+ actionError: { paddingHorizontal: 12, paddingVertical: 8, fontSize: 13 },
31
39
  });
@@ -18,7 +18,7 @@ export declare function SnapViewV2({ snap, handlers, loading, appearance, colors
18
18
  onValidationError?: (result: ValidationResult) => void;
19
19
  validationErrorFallback?: ReactNode;
20
20
  }): import("react").JSX.Element;
21
- export declare function SnapCardV2({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, }: {
21
+ export declare function SnapCardV2({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, }: {
22
22
  snap: SnapPage;
23
23
  handlers: SnapActionHandlers;
24
24
  loading?: boolean;
@@ -28,4 +28,5 @@ export declare function SnapCardV2({ snap, handlers, loading, appearance, colors
28
28
  showOverflowWarning?: boolean;
29
29
  onValidationError?: (result: ValidationResult) => void;
30
30
  validationErrorFallback?: ReactNode;
31
+ actionError?: string | null;
31
32
  }): import("react").JSX.Element;
@@ -50,26 +50,34 @@ export function SnapViewV2({ snap, handlers, loading = false, appearance = "dark
50
50
  return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapViewV2Inner, { snap: snap, handlers: handlers, loading: loading, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }) }));
51
51
  }
52
52
  // ─── SnapCardV2 (card frame + height limits) ─────────
53
- function SnapCardV2Inner({ snap, handlers, loading, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, }) {
53
+ function SnapCardV2Inner({ snap, handlers, loading, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, appearance, }) {
54
54
  const { colors } = useSnapTheme();
55
55
  const maxHeight = showOverflowWarning ? SNAP_WARNING_HEIGHT : SNAP_MAX_HEIGHT;
56
- return (_jsx(View, { style: cardStyles.frameRing, children: _jsxs(View, { style: [
57
- cardStyles.card,
58
- {
59
- borderRadius,
60
- maxHeight,
61
- borderColor: colors.border,
62
- backgroundColor: colors.surface,
63
- },
64
- ], children: [_jsx(View, { style: cardStyles.body, children: _jsx(SnapViewV2Inner, { snap: snap, handlers: handlers, loading: loading, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }) }), showOverflowWarning && (_jsxs(View, { style: cardStyles.warningOverlay, children: [_jsx(View, { style: cardStyles.warningLine }), _jsx(View, { style: cardStyles.warningLabel, children: _jsxs(Text, { style: cardStyles.warningLabelText, children: [SNAP_MAX_HEIGHT, "px"] }) })] }))] }) }));
56
+ return (_jsxs(_Fragment, { children: [_jsx(View, { style: cardStyles.frameRing, children: _jsxs(View, { style: [
57
+ cardStyles.card,
58
+ {
59
+ borderRadius,
60
+ maxHeight,
61
+ borderColor: colors.border,
62
+ backgroundColor: colors.surface,
63
+ },
64
+ ], children: [_jsx(View, { style: cardStyles.body, children: _jsx(SnapViewV2Inner, { snap: snap, handlers: handlers, loading: loading, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }) }), showOverflowWarning && (_jsxs(View, { style: cardStyles.warningOverlay, children: [_jsx(View, { style: cardStyles.warningLine }), _jsx(View, { style: cardStyles.warningLabel, children: _jsxs(Text, { style: cardStyles.warningLabelText, children: [SNAP_MAX_HEIGHT, "px"] }) })] }))] }) }), actionError && (_jsx(Text, { style: [
65
+ cardStyles.actionError,
66
+ {
67
+ color: appearance === "dark"
68
+ ? "rgba(255,100,100,0.9)"
69
+ : "rgba(200,0,0,0.8)",
70
+ },
71
+ ], children: actionError }))] }));
65
72
  }
66
- export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, }) {
67
- return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapCardV2Inner, { snap: snap, handlers: handlers, loading: loading, borderRadius: borderRadius, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }) }));
73
+ export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, }) {
74
+ return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapCardV2Inner, { snap: snap, handlers: handlers, loading: loading, borderRadius: borderRadius, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError, appearance: appearance }) }));
68
75
  }
69
76
  const cardStyles = StyleSheet.create({
70
77
  frameRing: { alignSelf: "stretch" },
71
78
  card: { overflow: "hidden", borderWidth: 1, minHeight: 120 },
72
79
  body: { paddingHorizontal: 16, paddingVertical: 16 },
80
+ actionError: { paddingHorizontal: 12, paddingVertical: 8, fontSize: 13 },
73
81
  warningOverlay: {
74
82
  position: "absolute",
75
83
  top: SNAP_MAX_HEIGHT,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farcaster/snap",
3
- "version": "1.16.3",
3
+ "version": "1.17.1",
4
4
  "description": "Farcaster Snaps 🫰",
5
5
  "repository": {
6
6
  "type": "git",
@@ -56,6 +56,7 @@ export function SnapCard({
56
56
  showOverflowWarning = false,
57
57
  onValidationError,
58
58
  validationErrorFallback,
59
+ actionError,
59
60
  }: {
60
61
  snap: SnapPage;
61
62
  handlers: SnapActionHandlers;
@@ -66,6 +67,8 @@ export function SnapCard({
66
67
  showOverflowWarning?: boolean;
67
68
  onValidationError?: (result: ValidationResult) => void;
68
69
  validationErrorFallback?: ReactNode;
70
+ /** Server-side action error message to display inline. */
71
+ actionError?: string | null;
69
72
  }) {
70
73
  if (snap.version === SPEC_VERSION_2) {
71
74
  return (
@@ -78,6 +81,7 @@ export function SnapCard({
78
81
  showOverflowWarning={showOverflowWarning}
79
82
  onValidationError={onValidationError}
80
83
  validationErrorFallback={validationErrorFallback}
84
+ actionError={actionError}
81
85
  />
82
86
  );
83
87
  }
@@ -89,6 +93,7 @@ export function SnapCard({
89
93
  loading={loading}
90
94
  appearance={appearance}
91
95
  maxWidth={maxWidth}
96
+ actionError={actionError}
92
97
  />
93
98
  );
94
99
  }
@@ -30,12 +30,14 @@ export function SnapCardV1({
30
30
  loading = false,
31
31
  appearance = "dark",
32
32
  maxWidth = 480,
33
+ actionError,
33
34
  }: {
34
35
  snap: SnapPage;
35
36
  handlers: SnapActionHandlers;
36
37
  loading?: boolean;
37
38
  appearance?: "light" | "dark";
38
39
  maxWidth?: number;
40
+ actionError?: string | null;
39
41
  }) {
40
42
  return (
41
43
  <div style={{ position: "relative", width: "100%", maxWidth }}>
@@ -45,6 +47,20 @@ export function SnapCardV1({
45
47
  loading={loading}
46
48
  appearance={appearance}
47
49
  />
50
+ {actionError && (
51
+ <div
52
+ style={{
53
+ padding: "8px 12px",
54
+ fontSize: 13,
55
+ color:
56
+ appearance === "dark"
57
+ ? "rgba(255,100,100,0.9)"
58
+ : "rgba(200,0,0,0.8)",
59
+ }}
60
+ >
61
+ {actionError}
62
+ </div>
63
+ )}
48
64
  </div>
49
65
  );
50
66
  }
@@ -94,6 +94,7 @@ export function SnapCardV2({
94
94
  showOverflowWarning = false,
95
95
  onValidationError,
96
96
  validationErrorFallback,
97
+ actionError,
97
98
  }: {
98
99
  snap: SnapPage;
99
100
  handlers: SnapActionHandlers;
@@ -103,11 +104,13 @@ export function SnapCardV2({
103
104
  showOverflowWarning?: boolean;
104
105
  onValidationError?: (result: ValidationResult) => void;
105
106
  validationErrorFallback?: ReactNode;
107
+ actionError?: string | null;
106
108
  }) {
107
109
  const maxHeight = showOverflowWarning ? SNAP_WARNING_HEIGHT : SNAP_MAX_HEIGHT;
108
110
  const bg = appearance === "dark" ? "rgba(0,0,0,0.85)" : "rgba(255,255,255,0.9)";
109
111
 
110
112
  return (
113
+ <>
111
114
  <div
112
115
  style={{
113
116
  position: "relative",
@@ -164,5 +167,21 @@ export function SnapCardV2({
164
167
  </div>
165
168
  )}
166
169
  </div>
170
+ {actionError && (
171
+ <div
172
+ style={{
173
+ maxWidth,
174
+ padding: "8px 12px",
175
+ fontSize: 13,
176
+ color:
177
+ appearance === "dark"
178
+ ? "rgba(255,100,100,0.9)"
179
+ : "rgba(200,0,0,0.8)",
180
+ }}
181
+ >
182
+ {actionError}
183
+ </div>
184
+ )}
185
+ </>
167
186
  );
168
187
  }
@@ -29,6 +29,7 @@ export function SnapCard({
29
29
  showOverflowWarning = false,
30
30
  onValidationError,
31
31
  validationErrorFallback,
32
+ actionError,
32
33
  }: {
33
34
  snap: SnapPage;
34
35
  handlers: SnapActionHandlers;
@@ -43,6 +44,8 @@ export function SnapCard({
43
44
  onValidationError?: (result: ValidationResult) => void;
44
45
  /** Custom fallback rendered when validation fails (v2 only). */
45
46
  validationErrorFallback?: ReactNode;
47
+ /** Server-side action error message to display inline. */
48
+ actionError?: string | null;
46
49
  }) {
47
50
  if (snap.version === SPEC_VERSION_2) {
48
51
  return (
@@ -56,6 +59,7 @@ export function SnapCard({
56
59
  showOverflowWarning={showOverflowWarning}
57
60
  onValidationError={onValidationError}
58
61
  validationErrorFallback={validationErrorFallback}
62
+ actionError={actionError}
59
63
  />
60
64
  );
61
65
  }
@@ -68,6 +72,7 @@ export function SnapCard({
68
72
  appearance={appearance}
69
73
  colors={colors}
70
74
  borderRadius={borderRadius}
75
+ actionError={actionError}
71
76
  />
72
77
  );
73
78
  }
@@ -1,4 +1,4 @@
1
- import { View, StyleSheet } from "react-native";
1
+ import { View, Text, StyleSheet } from "react-native";
2
2
  import { SnapThemeProvider, useSnapTheme, type SnapNativeColors } from "../theme";
3
3
  import { SnapViewCoreInner } from "../snap-view-core";
4
4
  import type { SnapPage, SnapActionHandlers } from "../types";
@@ -46,31 +46,52 @@ function SnapCardV1Inner({
46
46
  handlers,
47
47
  loading = false,
48
48
  borderRadius,
49
+ actionError,
50
+ appearance,
49
51
  }: {
50
52
  snap: SnapPage;
51
53
  handlers: SnapActionHandlers;
52
54
  loading?: boolean;
53
55
  borderRadius: number;
56
+ actionError?: string | null;
57
+ appearance: "light" | "dark";
54
58
  }) {
55
59
  const { colors } = useSnapTheme();
56
60
 
57
61
  return (
58
- <View style={cardStyles.frameRing}>
59
- <View
60
- style={[
61
- cardStyles.card,
62
- {
63
- borderRadius,
64
- borderColor: colors.border,
65
- backgroundColor: colors.surface,
66
- },
67
- ]}
68
- >
69
- <View style={cardStyles.body}>
70
- <SnapViewV1Inner snap={snap} handlers={handlers} loading={loading} />
62
+ <>
63
+ <View style={cardStyles.frameRing}>
64
+ <View
65
+ style={[
66
+ cardStyles.card,
67
+ {
68
+ borderRadius,
69
+ borderColor: colors.border,
70
+ backgroundColor: colors.surface,
71
+ },
72
+ ]}
73
+ >
74
+ <View style={cardStyles.body}>
75
+ <SnapViewV1Inner snap={snap} handlers={handlers} loading={loading} />
76
+ </View>
71
77
  </View>
72
78
  </View>
73
- </View>
79
+ {actionError && (
80
+ <Text
81
+ style={[
82
+ cardStyles.actionError,
83
+ {
84
+ color:
85
+ appearance === "dark"
86
+ ? "rgba(255,100,100,0.9)"
87
+ : "rgba(200,0,0,0.8)",
88
+ },
89
+ ]}
90
+ >
91
+ {actionError}
92
+ </Text>
93
+ )}
94
+ </>
74
95
  );
75
96
  }
76
97
 
@@ -81,6 +102,7 @@ export function SnapCardV1({
81
102
  appearance = "dark",
82
103
  colors,
83
104
  borderRadius = 16,
105
+ actionError,
84
106
  }: {
85
107
  snap: SnapPage;
86
108
  handlers: SnapActionHandlers;
@@ -88,6 +110,7 @@ export function SnapCardV1({
88
110
  appearance?: "light" | "dark";
89
111
  colors?: Partial<SnapNativeColors>;
90
112
  borderRadius?: number;
113
+ actionError?: string | null;
91
114
  }) {
92
115
  return (
93
116
  <SnapThemeProvider appearance={appearance} colors={colors}>
@@ -96,6 +119,8 @@ export function SnapCardV1({
96
119
  handlers={handlers}
97
120
  loading={loading}
98
121
  borderRadius={borderRadius}
122
+ actionError={actionError}
123
+ appearance={appearance}
99
124
  />
100
125
  </SnapThemeProvider>
101
126
  );
@@ -105,4 +130,5 @@ const cardStyles = StyleSheet.create({
105
130
  frameRing: { alignSelf: "stretch" },
106
131
  card: { overflow: "hidden", borderWidth: 1, minHeight: 120 },
107
132
  body: { paddingHorizontal: 16, paddingVertical: 16 },
133
+ actionError: { paddingHorizontal: 12, paddingVertical: 8, fontSize: 13 },
108
134
  });
@@ -121,6 +121,8 @@ function SnapCardV2Inner({
121
121
  showOverflowWarning,
122
122
  onValidationError,
123
123
  validationErrorFallback,
124
+ actionError,
125
+ appearance,
124
126
  }: {
125
127
  snap: SnapPage;
126
128
  handlers: SnapActionHandlers;
@@ -129,42 +131,61 @@ function SnapCardV2Inner({
129
131
  showOverflowWarning: boolean;
130
132
  onValidationError?: (result: ValidationResult) => void;
131
133
  validationErrorFallback?: ReactNode;
134
+ actionError?: string | null;
135
+ appearance: "light" | "dark";
132
136
  }) {
133
137
  const { colors } = useSnapTheme();
134
138
  const maxHeight = showOverflowWarning ? SNAP_WARNING_HEIGHT : SNAP_MAX_HEIGHT;
135
139
 
136
140
  return (
137
- <View style={cardStyles.frameRing}>
138
- <View
139
- style={[
140
- cardStyles.card,
141
- {
142
- borderRadius,
143
- maxHeight,
144
- borderColor: colors.border,
145
- backgroundColor: colors.surface,
146
- },
147
- ]}
148
- >
149
- <View style={cardStyles.body}>
150
- <SnapViewV2Inner
151
- snap={snap}
152
- handlers={handlers}
153
- loading={loading}
154
- onValidationError={onValidationError}
155
- validationErrorFallback={validationErrorFallback}
156
- />
157
- </View>
158
- {showOverflowWarning && (
159
- <View style={cardStyles.warningOverlay}>
160
- <View style={cardStyles.warningLine} />
161
- <View style={cardStyles.warningLabel}>
162
- <Text style={cardStyles.warningLabelText}>{SNAP_MAX_HEIGHT}px</Text>
163
- </View>
141
+ <>
142
+ <View style={cardStyles.frameRing}>
143
+ <View
144
+ style={[
145
+ cardStyles.card,
146
+ {
147
+ borderRadius,
148
+ maxHeight,
149
+ borderColor: colors.border,
150
+ backgroundColor: colors.surface,
151
+ },
152
+ ]}
153
+ >
154
+ <View style={cardStyles.body}>
155
+ <SnapViewV2Inner
156
+ snap={snap}
157
+ handlers={handlers}
158
+ loading={loading}
159
+ onValidationError={onValidationError}
160
+ validationErrorFallback={validationErrorFallback}
161
+ />
164
162
  </View>
165
- )}
163
+ {showOverflowWarning && (
164
+ <View style={cardStyles.warningOverlay}>
165
+ <View style={cardStyles.warningLine} />
166
+ <View style={cardStyles.warningLabel}>
167
+ <Text style={cardStyles.warningLabelText}>{SNAP_MAX_HEIGHT}px</Text>
168
+ </View>
169
+ </View>
170
+ )}
171
+ </View>
166
172
  </View>
167
- </View>
173
+ {actionError && (
174
+ <Text
175
+ style={[
176
+ cardStyles.actionError,
177
+ {
178
+ color:
179
+ appearance === "dark"
180
+ ? "rgba(255,100,100,0.9)"
181
+ : "rgba(200,0,0,0.8)",
182
+ },
183
+ ]}
184
+ >
185
+ {actionError}
186
+ </Text>
187
+ )}
188
+ </>
168
189
  );
169
190
  }
170
191
 
@@ -178,6 +199,7 @@ export function SnapCardV2({
178
199
  showOverflowWarning = false,
179
200
  onValidationError,
180
201
  validationErrorFallback,
202
+ actionError,
181
203
  }: {
182
204
  snap: SnapPage;
183
205
  handlers: SnapActionHandlers;
@@ -188,6 +210,7 @@ export function SnapCardV2({
188
210
  showOverflowWarning?: boolean;
189
211
  onValidationError?: (result: ValidationResult) => void;
190
212
  validationErrorFallback?: ReactNode;
213
+ actionError?: string | null;
191
214
  }) {
192
215
  return (
193
216
  <SnapThemeProvider appearance={appearance} colors={colors}>
@@ -199,6 +222,8 @@ export function SnapCardV2({
199
222
  showOverflowWarning={showOverflowWarning}
200
223
  onValidationError={onValidationError}
201
224
  validationErrorFallback={validationErrorFallback}
225
+ actionError={actionError}
226
+ appearance={appearance}
202
227
  />
203
228
  </SnapThemeProvider>
204
229
  );
@@ -208,6 +233,7 @@ const cardStyles = StyleSheet.create({
208
233
  frameRing: { alignSelf: "stretch" },
209
234
  card: { overflow: "hidden", borderWidth: 1, minHeight: 120 },
210
235
  body: { paddingHorizontal: 16, paddingVertical: 16 },
236
+ actionError: { paddingHorizontal: 12, paddingVertical: 8, fontSize: 13 },
211
237
  warningOverlay: {
212
238
  position: "absolute",
213
239
  top: SNAP_MAX_HEIGHT,