@farcaster/snap 1.19.0 → 1.21.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.
- package/dist/react/components/action-button.js +2 -2
- package/dist/react/index.d.ts +4 -1
- package/dist/react/index.js +3 -3
- package/dist/react/v1/snap-view.d.ts +2 -1
- package/dist/react/v1/snap-view.js +77 -2
- package/dist/react/v2/snap-view.d.ts +2 -1
- package/dist/react/v2/snap-view.js +11 -3
- package/dist/react-native/components/snap-action-button.js +3 -3
- package/dist/react-native/components/snap-bar-chart.js +1 -1
- package/dist/react-native/components/snap-image.js +0 -1
- package/dist/react-native/components/snap-progress.js +1 -1
- package/dist/react-native/components/snap-text.js +1 -1
- package/dist/react-native/index.d.ts +3 -1
- package/dist/react-native/index.js +3 -3
- package/dist/react-native/theme.js +6 -6
- package/dist/react-native/types.d.ts +1 -0
- package/dist/react-native/v1/snap-view.d.ts +2 -1
- package/dist/react-native/v1/snap-view.js +68 -11
- package/dist/react-native/v2/snap-view.d.ts +2 -1
- package/dist/react-native/v2/snap-view.js +25 -21
- package/dist/ui/catalog.d.ts +6 -1
- package/dist/ui/catalog.js +6 -5
- package/llms.txt +3 -2
- package/package.json +1 -1
- package/src/react/components/action-button.tsx +2 -2
- package/src/react/index.tsx +6 -0
- package/src/react/v1/snap-view.tsx +117 -7
- package/src/react/v2/snap-view.tsx +13 -1
- package/src/react-native/components/snap-action-button.tsx +3 -3
- package/src/react-native/components/snap-bar-chart.tsx +1 -1
- package/src/react-native/components/snap-image.tsx +0 -1
- package/src/react-native/components/snap-progress.tsx +1 -1
- package/src/react-native/components/snap-text.tsx +1 -1
- package/src/react-native/index.tsx +5 -0
- package/src/react-native/theme.tsx +6 -6
- package/src/react-native/types.ts +1 -0
- package/src/react-native/v1/snap-view.tsx +102 -7
- package/src/react-native/v2/snap-view.tsx +52 -40
- package/src/ui/catalog.ts +6 -5
|
@@ -10,9 +10,9 @@ function isExternalLinkAction(on) {
|
|
|
10
10
|
if (!on)
|
|
11
11
|
return false;
|
|
12
12
|
const press = on.press;
|
|
13
|
-
if (!press
|
|
13
|
+
if (!press)
|
|
14
14
|
return false;
|
|
15
|
-
return press.
|
|
15
|
+
return press.action === "open_url";
|
|
16
16
|
}
|
|
17
17
|
export function SnapActionButton({ element, emit, }) {
|
|
18
18
|
const { props } = element;
|
package/dist/react/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export type SnapPage = {
|
|
|
15
15
|
export type SnapActionHandlers = {
|
|
16
16
|
submit: (target: string, inputs: Record<string, JsonValue>) => void;
|
|
17
17
|
open_url: (target: string) => void;
|
|
18
|
+
open_snap: (target: string) => void;
|
|
18
19
|
open_mini_app: (target: string) => void;
|
|
19
20
|
view_cast: (params: {
|
|
20
21
|
hash: string;
|
|
@@ -41,7 +42,7 @@ export type SnapActionHandlers = {
|
|
|
41
42
|
buyToken?: string;
|
|
42
43
|
}) => void;
|
|
43
44
|
};
|
|
44
|
-
export declare function SnapCard({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, actionError, }: {
|
|
45
|
+
export declare function SnapCard({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, actionError, plain, }: {
|
|
45
46
|
snap: SnapPage;
|
|
46
47
|
handlers: SnapActionHandlers;
|
|
47
48
|
loading?: boolean;
|
|
@@ -53,4 +54,6 @@ export declare function SnapCard({ snap, handlers, loading, appearance, maxWidth
|
|
|
53
54
|
validationErrorFallback?: ReactNode;
|
|
54
55
|
/** Server-side action error message to display inline. */
|
|
55
56
|
actionError?: string | null;
|
|
57
|
+
/** When true, renders without card frame (no border, background, or padding). */
|
|
58
|
+
plain?: boolean;
|
|
56
59
|
}): import("react/jsx-runtime").JSX.Element;
|
package/dist/react/index.js
CHANGED
|
@@ -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, actionError, }) {
|
|
7
|
+
export function SnapCard({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, plain = false, }) {
|
|
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, actionError: actionError }));
|
|
9
|
+
return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError, plain: plain }));
|
|
10
10
|
}
|
|
11
|
-
return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, actionError: actionError }));
|
|
11
|
+
return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, maxWidth: maxWidth, actionError: actionError, plain: plain }));
|
|
12
12
|
}
|
|
@@ -5,11 +5,12 @@ 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, actionError, }: {
|
|
8
|
+
export declare function SnapCardV1({ snap, handlers, loading, appearance, maxWidth, actionError, plain, }: {
|
|
9
9
|
snap: SnapPage;
|
|
10
10
|
handlers: SnapActionHandlers;
|
|
11
11
|
loading?: boolean;
|
|
12
12
|
appearance?: "light" | "dark";
|
|
13
13
|
maxWidth?: number;
|
|
14
14
|
actionError?: string | null;
|
|
15
|
+
plain?: boolean;
|
|
15
16
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,11 +1,86 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useEffect, useRef, useState } from "react";
|
|
3
4
|
import { SnapViewCore } from "../snap-view-core.js";
|
|
5
|
+
const SNAP_MAX_HEIGHT = 500;
|
|
4
6
|
export function SnapViewV1({ snap, handlers, loading = false, appearance = "dark", }) {
|
|
5
7
|
return (_jsx(SnapViewCore, { snap: snap, handlers: handlers, loading: loading, appearance: appearance }));
|
|
6
8
|
}
|
|
7
|
-
export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, actionError, }) {
|
|
8
|
-
|
|
9
|
+
export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, actionError, plain = false, }) {
|
|
10
|
+
const isDark = appearance === "dark";
|
|
11
|
+
const borderColor = isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)";
|
|
12
|
+
const surfaceBg = isDark ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.02)";
|
|
13
|
+
const toggleBg = isDark ? "rgba(255,255,255,0.06)" : "rgba(0,0,0,0.05)";
|
|
14
|
+
const toggleBgHover = isDark
|
|
15
|
+
? "rgba(255,255,255,0.1)"
|
|
16
|
+
: "rgba(0,0,0,0.08)";
|
|
17
|
+
const toggleText = isDark ? "rgba(255,255,255,0.82)" : "rgba(0,0,0,0.72)";
|
|
18
|
+
const contentRef = useRef(null);
|
|
19
|
+
const [isExpandable, setIsExpandable] = useState(false);
|
|
20
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
setIsExpanded(false);
|
|
23
|
+
}, [snap]);
|
|
24
|
+
useEffect(() => {
|
|
25
|
+
const node = contentRef.current;
|
|
26
|
+
if (!node)
|
|
27
|
+
return;
|
|
28
|
+
const measure = () => {
|
|
29
|
+
setIsExpandable(node.scrollHeight > SNAP_MAX_HEIGHT + 1);
|
|
30
|
+
};
|
|
31
|
+
measure();
|
|
32
|
+
if (typeof ResizeObserver === "undefined")
|
|
33
|
+
return;
|
|
34
|
+
const observer = new ResizeObserver(() => {
|
|
35
|
+
measure();
|
|
36
|
+
});
|
|
37
|
+
observer.observe(node);
|
|
38
|
+
return () => observer.disconnect();
|
|
39
|
+
}, [snap, plain]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!isExpandable) {
|
|
42
|
+
setIsExpanded(false);
|
|
43
|
+
}
|
|
44
|
+
}, [isExpandable]);
|
|
45
|
+
const isClipped = isExpandable && !isExpanded;
|
|
46
|
+
return (_jsxs("div", { style: {
|
|
47
|
+
position: "relative",
|
|
48
|
+
width: "100%",
|
|
49
|
+
maxWidth,
|
|
50
|
+
overflow: "hidden",
|
|
51
|
+
...(plain ? {} : {
|
|
52
|
+
borderRadius: 16,
|
|
53
|
+
border: `1px solid ${borderColor}`,
|
|
54
|
+
backgroundColor: surfaceBg,
|
|
55
|
+
}),
|
|
56
|
+
}, children: [_jsx("div", { style: isClipped
|
|
57
|
+
? {
|
|
58
|
+
maxHeight: SNAP_MAX_HEIGHT,
|
|
59
|
+
overflow: "hidden",
|
|
60
|
+
}
|
|
61
|
+
: undefined, children: _jsx("div", { ref: contentRef, style: plain ? undefined : { padding: 16 }, children: _jsx(SnapViewV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance }) }) }), isExpandable ? (_jsx("div", { style: {
|
|
62
|
+
display: "flex",
|
|
63
|
+
justifyContent: "center",
|
|
64
|
+
padding: plain ? "8px 0 0" : "10px 16px 12px",
|
|
65
|
+
...(plain
|
|
66
|
+
? {}
|
|
67
|
+
: { borderTop: `1px solid ${borderColor}` }),
|
|
68
|
+
}, children: _jsx("button", { type: "button", "aria-expanded": isExpanded, onClick: () => setIsExpanded((value) => !value), style: {
|
|
69
|
+
appearance: "none",
|
|
70
|
+
border: "none",
|
|
71
|
+
borderRadius: 9999,
|
|
72
|
+
backgroundColor: toggleBg,
|
|
73
|
+
color: toggleText,
|
|
74
|
+
padding: "6px 10px",
|
|
75
|
+
fontSize: 13,
|
|
76
|
+
lineHeight: "18px",
|
|
77
|
+
fontWeight: 600,
|
|
78
|
+
cursor: "pointer",
|
|
79
|
+
}, onMouseEnter: (event) => {
|
|
80
|
+
event.currentTarget.style.backgroundColor = toggleBgHover;
|
|
81
|
+
}, onMouseLeave: (event) => {
|
|
82
|
+
event.currentTarget.style.backgroundColor = toggleBg;
|
|
83
|
+
}, children: isExpanded ? "Show less" : "Show more" }) })) : null, actionError && (_jsx("div", { style: {
|
|
9
84
|
padding: "8px 12px",
|
|
10
85
|
fontSize: 13,
|
|
11
86
|
color: appearance === "dark"
|
|
@@ -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, actionError, }: {
|
|
12
|
+
export declare function SnapCardV2({ snap, handlers, loading, appearance, maxWidth, showOverflowWarning, onValidationError, validationErrorFallback, actionError, plain, }: {
|
|
13
13
|
snap: SnapPage;
|
|
14
14
|
handlers: SnapActionHandlers;
|
|
15
15
|
loading?: boolean;
|
|
@@ -19,4 +19,5 @@ export declare function SnapCardV2({ snap, handlers, loading, appearance, maxWid
|
|
|
19
19
|
onValidationError?: (result: ValidationResult) => void;
|
|
20
20
|
validationErrorFallback?: ReactNode;
|
|
21
21
|
actionError?: string | null;
|
|
22
|
+
plain?: boolean;
|
|
22
23
|
}): import("react/jsx-runtime").JSX.Element;
|
|
@@ -42,16 +42,24 @@ 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, actionError, }) {
|
|
45
|
+
export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", maxWidth = 480, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, plain = false, }) {
|
|
46
46
|
const maxHeight = showOverflowWarning ? SNAP_WARNING_HEIGHT : SNAP_MAX_HEIGHT;
|
|
47
|
-
const
|
|
47
|
+
const isDark = appearance === "dark";
|
|
48
|
+
const bg = isDark ? "rgba(0,0,0,0.85)" : "rgba(255,255,255,0.9)";
|
|
49
|
+
const borderColor = isDark ? "rgba(255,255,255,0.1)" : "rgba(0,0,0,0.1)";
|
|
50
|
+
const surfaceBg = isDark ? "rgba(255,255,255,0.05)" : "rgba(0,0,0,0.02)";
|
|
48
51
|
return (_jsxs(_Fragment, { children: [_jsxs("div", { style: {
|
|
49
52
|
position: "relative",
|
|
50
53
|
width: "100%",
|
|
51
54
|
maxWidth,
|
|
52
55
|
maxHeight,
|
|
53
56
|
overflow: "hidden",
|
|
54
|
-
|
|
57
|
+
...(plain ? {} : {
|
|
58
|
+
borderRadius: 16,
|
|
59
|
+
border: `1px solid ${borderColor}`,
|
|
60
|
+
backgroundColor: surfaceBg,
|
|
61
|
+
}),
|
|
62
|
+
}, children: [_jsx("div", { style: plain ? undefined : { padding: 16 }, children: _jsx(SnapViewV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }) }), showOverflowWarning && (_jsxs("div", { style: {
|
|
55
63
|
position: "absolute",
|
|
56
64
|
top: SNAP_MAX_HEIGHT,
|
|
57
65
|
left: 0,
|
|
@@ -8,9 +8,9 @@ function isExternalLinkAction(on) {
|
|
|
8
8
|
if (!on)
|
|
9
9
|
return false;
|
|
10
10
|
const press = on.press;
|
|
11
|
-
if (!press
|
|
11
|
+
if (!press)
|
|
12
12
|
return false;
|
|
13
|
-
return press.
|
|
13
|
+
return press.action === "open_url";
|
|
14
14
|
}
|
|
15
15
|
export function SnapActionButton({ element, emit, }) {
|
|
16
16
|
const { accentHex } = useSnapPalette();
|
|
@@ -50,7 +50,7 @@ export function SnapActionButton({ element, emit, }) {
|
|
|
50
50
|
: null, _jsx(Text, { style: { color: textColor, fontSize: 14, lineHeight: 18, fontWeight: "600" }, children: label }), showExternalIcon ? (_jsx(ExternalLink, { size: 14, color: iconColor, style: { opacity: 0.6 } })) : null] }) }));
|
|
51
51
|
}
|
|
52
52
|
const styles = StyleSheet.create({
|
|
53
|
-
outer: {
|
|
53
|
+
outer: { minWidth: 0 },
|
|
54
54
|
btn: {
|
|
55
55
|
paddingHorizontal: 16,
|
|
56
56
|
borderRadius: 10,
|
|
@@ -30,7 +30,7 @@ export function SnapBarChart({ element: { props }, }) {
|
|
|
30
30
|
}) }));
|
|
31
31
|
}
|
|
32
32
|
const styles = StyleSheet.create({
|
|
33
|
-
wrap: {
|
|
33
|
+
wrap: { width: "100%", gap: 8 },
|
|
34
34
|
row: { flexDirection: "row", alignItems: "center", gap: 8 },
|
|
35
35
|
label: { width: 80, fontSize: 12, lineHeight: 16, textAlign: "right" },
|
|
36
36
|
track: { flex: 1, height: 10, borderRadius: 9999, overflow: "hidden" },
|
|
@@ -12,7 +12,7 @@ export function SnapProgress({ element: { props }, }) {
|
|
|
12
12
|
return (_jsxs(View, { style: styles.wrap, children: [label ? (_jsx(Text, { style: [styles.label, { color: colors.textSecondary }], children: label })) : null, _jsx(View, { style: [styles.track, { backgroundColor: colors.muted }], children: _jsx(View, { style: [styles.fill, { width: `${percent}%`, backgroundColor: accentHex }] }) })] }));
|
|
13
13
|
}
|
|
14
14
|
const styles = StyleSheet.create({
|
|
15
|
-
wrap: {
|
|
15
|
+
wrap: { width: "100%", gap: 4 },
|
|
16
16
|
label: { fontSize: 13, lineHeight: 18 },
|
|
17
17
|
track: {
|
|
18
18
|
height: 10,
|
|
@@ -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, actionError, }: {
|
|
10
|
+
export declare function SnapCard({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, plain, }: {
|
|
11
11
|
snap: SnapPage;
|
|
12
12
|
handlers: SnapActionHandlers;
|
|
13
13
|
loading?: boolean;
|
|
@@ -23,4 +23,6 @@ export declare function SnapCard({ snap, handlers, loading, appearance, colors,
|
|
|
23
23
|
validationErrorFallback?: ReactNode;
|
|
24
24
|
/** Server-side action error message to display inline. */
|
|
25
25
|
actionError?: string | null;
|
|
26
|
+
/** When true, renders without card frame (no border, background, or padding). */
|
|
27
|
+
plain?: boolean;
|
|
26
28
|
}): 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, actionError, }) {
|
|
10
|
+
export function SnapCard({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, plain = false, }) {
|
|
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, actionError: actionError }));
|
|
12
|
+
return (_jsx(SnapCardV2, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, showOverflowWarning: showOverflowWarning, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback, actionError: actionError, plain: plain }));
|
|
13
13
|
}
|
|
14
|
-
return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, actionError: actionError }));
|
|
14
|
+
return (_jsx(SnapCardV1, { snap: snap, handlers: handlers, loading: loading, appearance: appearance, colors: colors, borderRadius: borderRadius, actionError: actionError, plain: plain }));
|
|
15
15
|
}
|
|
@@ -3,9 +3,9 @@ import { createContext, useContext, useMemo } from "react";
|
|
|
3
3
|
const DEFAULT_LIGHT = {
|
|
4
4
|
bg: "#dfe3e8",
|
|
5
5
|
surface: "#ffffff",
|
|
6
|
-
text: "
|
|
7
|
-
textSecondary: "
|
|
8
|
-
border: "
|
|
6
|
+
text: "rgba(0,0,0,0.9)",
|
|
7
|
+
textSecondary: "rgba(0,0,0,0.5)",
|
|
8
|
+
border: "rgba(0,0,0,0.1)",
|
|
9
9
|
inputBg: "rgba(0,0,0,0.06)",
|
|
10
10
|
muted: "rgba(0,0,0,0.08)",
|
|
11
11
|
mutedSubtle: "rgba(0,0,0,0.04)",
|
|
@@ -15,9 +15,9 @@ const DEFAULT_LIGHT = {
|
|
|
15
15
|
const DEFAULT_DARK = {
|
|
16
16
|
bg: "#111318",
|
|
17
17
|
surface: "#1a1d24",
|
|
18
|
-
text: "
|
|
19
|
-
textSecondary: "
|
|
20
|
-
border: "
|
|
18
|
+
text: "rgba(255,255,255,0.9)",
|
|
19
|
+
textSecondary: "rgba(255,255,255,0.5)",
|
|
20
|
+
border: "rgba(255,255,255,0.1)",
|
|
21
21
|
inputBg: "rgba(255,255,255,0.04)",
|
|
22
22
|
muted: "rgba(255,255,255,0.06)",
|
|
23
23
|
mutedSubtle: "rgba(255,255,255,0.03)",
|
|
@@ -13,6 +13,7 @@ export type SnapPage = {
|
|
|
13
13
|
export type SnapActionHandlers = {
|
|
14
14
|
submit: (target: string, inputs: Record<string, JsonValue>) => void;
|
|
15
15
|
open_url: (target: string) => void;
|
|
16
|
+
open_snap: (target: string) => void;
|
|
16
17
|
open_mini_app: (target: string) => void;
|
|
17
18
|
view_cast: (params: {
|
|
18
19
|
hash: string;
|
|
@@ -12,7 +12,7 @@ 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, actionError, }: {
|
|
15
|
+
export declare function SnapCardV1({ snap, handlers, loading, appearance, colors, borderRadius, actionError, plain, }: {
|
|
16
16
|
snap: SnapPage;
|
|
17
17
|
handlers: SnapActionHandlers;
|
|
18
18
|
loading?: boolean;
|
|
@@ -20,4 +20,5 @@ export declare function SnapCardV1({ snap, handlers, loading, appearance, colors
|
|
|
20
20
|
colors?: Partial<SnapNativeColors>;
|
|
21
21
|
borderRadius?: number;
|
|
22
22
|
actionError?: string | null;
|
|
23
|
+
plain?: boolean;
|
|
23
24
|
}): import("react").JSX.Element;
|
|
@@ -1,25 +1,56 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
2
|
-
import {
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from "react";
|
|
3
|
+
import { View, Text, StyleSheet, Pressable } from "react-native";
|
|
3
4
|
import { SnapThemeProvider, useSnapTheme } from "../theme.js";
|
|
4
5
|
import { SnapViewCoreInner } from "../snap-view-core.js";
|
|
5
|
-
|
|
6
|
+
const SNAP_MAX_HEIGHT = 500;
|
|
7
|
+
// ─── SnapViewV1 (no validation) ──────────────────────
|
|
6
8
|
export function SnapViewV1Inner({ snap, handlers, loading = false, }) {
|
|
7
9
|
return (_jsx(SnapViewCoreInner, { snap: snap, handlers: handlers, loading: loading }));
|
|
8
10
|
}
|
|
9
11
|
export function SnapViewV1({ snap, handlers, loading = false, appearance = "dark", colors, }) {
|
|
10
12
|
return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapViewV1Inner, { snap: snap, handlers: handlers, loading: loading }) }));
|
|
11
13
|
}
|
|
12
|
-
// ─── SnapCardV1 (card frame
|
|
13
|
-
function SnapCardV1Inner({ snap, handlers, loading = false, borderRadius, actionError, appearance, }) {
|
|
14
|
+
// ─── SnapCardV1 (card frame with expandable clipping) ──
|
|
15
|
+
function SnapCardV1Inner({ snap, handlers, loading = false, borderRadius, actionError, appearance, plain, }) {
|
|
14
16
|
const { colors } = useSnapTheme();
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
const [contentHeight, setContentHeight] = useState(0);
|
|
18
|
+
const [isExpanded, setIsExpanded] = useState(false);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setIsExpanded(false);
|
|
21
|
+
setContentHeight(0);
|
|
22
|
+
}, [snap]);
|
|
23
|
+
const isExpandable = contentHeight > SNAP_MAX_HEIGHT + 1;
|
|
24
|
+
const isClipped = isExpandable && !isExpanded;
|
|
25
|
+
return (_jsxs(_Fragment, { children: [_jsx(View, { style: cardStyles.frameRing, children: _jsxs(View, { style: [
|
|
26
|
+
plain ? undefined : cardStyles.card,
|
|
27
|
+
plain ? undefined : {
|
|
18
28
|
borderRadius,
|
|
19
29
|
borderColor: colors.border,
|
|
20
30
|
backgroundColor: colors.surface,
|
|
21
31
|
},
|
|
22
|
-
], children: _jsx(View, { style:
|
|
32
|
+
], children: [_jsx(View, { style: isClipped ? { maxHeight: SNAP_MAX_HEIGHT, overflow: "hidden" } : undefined, children: _jsx(View, { collapsable: false, onLayout: (event) => {
|
|
33
|
+
const nextHeight = Math.round(event.nativeEvent.layout.height);
|
|
34
|
+
setContentHeight((currentHeight) => isClipped
|
|
35
|
+
? Math.max(currentHeight, nextHeight)
|
|
36
|
+
: currentHeight === nextHeight
|
|
37
|
+
? currentHeight
|
|
38
|
+
: nextHeight);
|
|
39
|
+
}, style: plain ? undefined : cardStyles.body, children: _jsx(SnapViewV1Inner, { snap: snap, handlers: handlers, loading: loading }) }) }), isExpandable ? (_jsx(View, { style: [
|
|
40
|
+
cardStyles.expandRow,
|
|
41
|
+
plain
|
|
42
|
+
? cardStyles.expandRowPlain
|
|
43
|
+
: { borderTopColor: colors.border },
|
|
44
|
+
], children: _jsx(Pressable, { style: ({ pressed }) => [
|
|
45
|
+
cardStyles.expandButton,
|
|
46
|
+
{
|
|
47
|
+
backgroundColor: pressed
|
|
48
|
+
? colors.mutedHover
|
|
49
|
+
: colors.muted,
|
|
50
|
+
},
|
|
51
|
+
], onPress: () => {
|
|
52
|
+
setIsExpanded((value) => !value);
|
|
53
|
+
}, children: _jsx(Text, { style: [cardStyles.expandButtonText, { color: colors.text }], children: isExpanded ? "Show less" : "Show more" }) }) })) : null] }) }), actionError && (_jsx(Text, { style: [
|
|
23
54
|
cardStyles.actionError,
|
|
24
55
|
{
|
|
25
56
|
color: appearance === "dark"
|
|
@@ -28,12 +59,38 @@ function SnapCardV1Inner({ snap, handlers, loading = false, borderRadius, action
|
|
|
28
59
|
},
|
|
29
60
|
], children: actionError }))] }));
|
|
30
61
|
}
|
|
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 }) }));
|
|
62
|
+
export function SnapCardV1({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, actionError, plain = false, }) {
|
|
63
|
+
return (_jsx(SnapThemeProvider, { appearance: appearance, colors: colors, children: _jsx(SnapCardV1Inner, { snap: snap, handlers: handlers, loading: loading, borderRadius: borderRadius, actionError: actionError, appearance: appearance, plain: plain }) }));
|
|
33
64
|
}
|
|
34
65
|
const cardStyles = StyleSheet.create({
|
|
35
66
|
frameRing: { alignSelf: "stretch" },
|
|
36
67
|
card: { overflow: "hidden", borderWidth: 1, minHeight: 120 },
|
|
37
68
|
body: { paddingHorizontal: 16, paddingVertical: 16 },
|
|
69
|
+
expandRow: {
|
|
70
|
+
alignItems: "center",
|
|
71
|
+
paddingHorizontal: 16,
|
|
72
|
+
paddingTop: 10,
|
|
73
|
+
paddingBottom: 12,
|
|
74
|
+
borderTopWidth: StyleSheet.hairlineWidth,
|
|
75
|
+
},
|
|
76
|
+
expandRowPlain: {
|
|
77
|
+
paddingHorizontal: 0,
|
|
78
|
+
paddingTop: 8,
|
|
79
|
+
paddingBottom: 0,
|
|
80
|
+
borderTopWidth: 0,
|
|
81
|
+
},
|
|
82
|
+
expandButton: {
|
|
83
|
+
minWidth: 92,
|
|
84
|
+
alignItems: "center",
|
|
85
|
+
justifyContent: "center",
|
|
86
|
+
borderRadius: 9999,
|
|
87
|
+
paddingHorizontal: 10,
|
|
88
|
+
paddingVertical: 6,
|
|
89
|
+
},
|
|
90
|
+
expandButtonText: {
|
|
91
|
+
fontSize: 13,
|
|
92
|
+
lineHeight: 18,
|
|
93
|
+
fontWeight: "600",
|
|
94
|
+
},
|
|
38
95
|
actionError: { paddingHorizontal: 12, paddingVertical: 8, fontSize: 13 },
|
|
39
96
|
});
|
|
@@ -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, actionError, }: {
|
|
21
|
+
export declare function SnapCardV2({ snap, handlers, loading, appearance, colors, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, plain, }: {
|
|
22
22
|
snap: SnapPage;
|
|
23
23
|
handlers: SnapActionHandlers;
|
|
24
24
|
loading?: boolean;
|
|
@@ -29,4 +29,5 @@ export declare function SnapCardV2({ snap, handlers, loading, appearance, colors
|
|
|
29
29
|
onValidationError?: (result: ValidationResult) => void;
|
|
30
30
|
validationErrorFallback?: ReactNode;
|
|
31
31
|
actionError?: string | null;
|
|
32
|
+
plain?: boolean;
|
|
32
33
|
}): import("react").JSX.Element;
|
|
@@ -50,32 +50,36 @@ 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, actionError, appearance, }) {
|
|
53
|
+
function SnapCardV2Inner({ snap, handlers, loading, borderRadius, showOverflowWarning, onValidationError, validationErrorFallback, actionError, appearance, plain, }) {
|
|
54
54
|
const { colors } = useSnapTheme();
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
55
|
+
const clipHeight = showOverflowWarning ? undefined : SNAP_MAX_HEIGHT;
|
|
56
|
+
const content = (_jsx(SnapViewV2Inner, { snap: snap, handlers: handlers, loading: loading, onValidationError: onValidationError, validationErrorFallback: validationErrorFallback }));
|
|
57
|
+
if (plain) {
|
|
58
|
+
return content;
|
|
59
|
+
}
|
|
60
|
+
return (_jsxs(_Fragment, { children: [_jsxs(View, { style: {
|
|
61
|
+
borderRadius,
|
|
62
|
+
borderWidth: 1,
|
|
63
|
+
borderColor: colors.border,
|
|
64
|
+
backgroundColor: colors.surface,
|
|
65
|
+
maxHeight: clipHeight,
|
|
66
|
+
overflow: "hidden",
|
|
67
|
+
minHeight: 120,
|
|
68
|
+
}, children: [_jsx(View, { style: { paddingHorizontal: 16, paddingVertical: 16 }, children: content }), showOverflowWarning && (_jsxs(View, { style: { position: "absolute", top: SNAP_MAX_HEIGHT, left: 0, right: 0, bottom: 0, zIndex: 10, pointerEvents: "none" }, children: [_jsx(View, { style: { height: 1, borderTopWidth: 1, borderStyle: "dashed", borderColor: "rgba(255,100,100,0.6)" } }), _jsx(View, { style: { position: "absolute", top: -10, right: 4, backgroundColor: "rgba(0,0,0,0.7)", paddingHorizontal: 4, paddingVertical: 1, borderRadius: 3 }, children: _jsxs(Text, { style: { fontSize: 10, color: "rgba(255,100,100,0.7)", fontFamily: Platform.select({ ios: "Menlo", default: "monospace" }) }, children: [SNAP_MAX_HEIGHT, "px"] }) }), _jsx(View, { style: { flex: 1, backgroundColor: "rgba(255,50,50,0.15)" } })] }))] }), actionError && (_jsx(Text, { style: {
|
|
69
|
+
paddingHorizontal: 12,
|
|
70
|
+
paddingVertical: 8,
|
|
71
|
+
fontSize: 13,
|
|
72
|
+
color: appearance === "dark"
|
|
73
|
+
? "rgba(255,100,100,0.9)"
|
|
74
|
+
: "rgba(200,0,0,0.8)",
|
|
75
|
+
}, children: actionError }))] }));
|
|
72
76
|
}
|
|
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 }) }));
|
|
77
|
+
export function SnapCardV2({ snap, handlers, loading = false, appearance = "dark", colors, borderRadius = 16, showOverflowWarning = false, onValidationError, validationErrorFallback, actionError, plain = false, }) {
|
|
78
|
+
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, plain: plain }) }));
|
|
75
79
|
}
|
|
76
80
|
const cardStyles = StyleSheet.create({
|
|
77
81
|
frameRing: { alignSelf: "stretch" },
|
|
78
|
-
card: {
|
|
82
|
+
card: { borderWidth: 1, minHeight: 120, overflow: "hidden" },
|
|
79
83
|
body: { paddingHorizontal: 16, paddingVertical: 16 },
|
|
80
84
|
actionError: { paddingHorizontal: 12, paddingVertical: 8, fontSize: 13 },
|
|
81
85
|
warningOverlay: {
|
package/dist/ui/catalog.d.ts
CHANGED
|
@@ -418,7 +418,12 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
418
418
|
description: string;
|
|
419
419
|
params: z.ZodObject<{
|
|
420
420
|
target: z.ZodString;
|
|
421
|
-
|
|
421
|
+
}, z.core.$strip>;
|
|
422
|
+
};
|
|
423
|
+
open_snap: {
|
|
424
|
+
description: string;
|
|
425
|
+
params: z.ZodObject<{
|
|
426
|
+
target: z.ZodString;
|
|
422
427
|
}, z.core.$strip>;
|
|
423
428
|
};
|
|
424
429
|
open_mini_app: {
|
package/dist/ui/catalog.js
CHANGED
|
@@ -99,11 +99,12 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
99
99
|
params: z.object({ target: z.string() }),
|
|
100
100
|
},
|
|
101
101
|
open_url: {
|
|
102
|
-
description: "Open
|
|
103
|
-
params: z.object({
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
description: "Open external URL in browser.",
|
|
103
|
+
params: z.object({ target: z.string() }),
|
|
104
|
+
},
|
|
105
|
+
open_snap: {
|
|
106
|
+
description: "Open a snap URL inline. The client renders the target as a snap rather than opening a browser.",
|
|
107
|
+
params: z.object({ target: z.string() }),
|
|
107
108
|
},
|
|
108
109
|
open_mini_app: {
|
|
109
110
|
description: "Open target URL as a Farcaster mini app.",
|
package/llms.txt
CHANGED
|
@@ -153,14 +153,15 @@ Field values are sent in POST `inputs[name]` when a `submit` action fires.
|
|
|
153
153
|
- `label` (string, optional, max 60)
|
|
154
154
|
- POST value: string (single) or string[] (multiple)
|
|
155
155
|
|
|
156
|
-
## Actions (
|
|
156
|
+
## Actions (10 types)
|
|
157
157
|
|
|
158
158
|
Bound to buttons via `on.press`:
|
|
159
159
|
|
|
160
160
|
| Action | Params | Description |
|
|
161
161
|
|--------|--------|-------------|
|
|
162
162
|
| `submit` | `target` (URL) | POST to server, get next page |
|
|
163
|
-
| `open_url` | `target` (URL) | Open in
|
|
163
|
+
| `open_url` | `target` (URL) | Open external URL in browser |
|
|
164
|
+
| `open_snap` | `target` (URL) | Open a snap URL inline |
|
|
164
165
|
| `open_mini_app` | `target` (URL) | Open as Farcaster mini app |
|
|
165
166
|
| `view_cast` | `hash` (string) | Navigate to a cast |
|
|
166
167
|
| `view_profile` | `fid` (number) | Navigate to a profile |
|
package/package.json
CHANGED
|
@@ -14,8 +14,8 @@ function isExternalLinkAction(
|
|
|
14
14
|
const press = on.press as
|
|
15
15
|
| { action?: string; params?: Record<string, unknown> }
|
|
16
16
|
| undefined;
|
|
17
|
-
if (!press
|
|
18
|
-
return press.
|
|
17
|
+
if (!press) return false;
|
|
18
|
+
return press.action === "open_url";
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function SnapActionButton({
|