@farcaster/snap 1.8.0 → 1.9.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 -4
- package/dist/react/components/badge.js +2 -3
- package/dist/react/components/item.js +1 -1
- package/dist/react/components/text.js +3 -9
- package/dist/react-native/components/snap-action-button.js +6 -12
- package/dist/react-native/components/snap-badge.js +5 -3
- package/dist/react-native/components/snap-item.js +1 -5
- package/dist/react-native/components/snap-text.js +0 -2
- package/dist/ui/badge.d.ts +5 -0
- package/dist/ui/badge.js +2 -0
- package/dist/ui/button.d.ts +2 -4
- package/dist/ui/button.js +1 -1
- package/dist/ui/catalog.d.ts +5 -8
- package/dist/ui/catalog.js +2 -2
- package/dist/ui/image.d.ts +1 -2
- package/dist/ui/image.js +1 -1
- package/dist/ui/item.d.ts +1 -3
- package/dist/ui/item.js +1 -1
- package/dist/ui/text.d.ts +2 -4
- package/dist/ui/text.js +2 -2
- package/llms.txt +172 -0
- package/package.json +3 -2
- package/src/react/components/action-button.tsx +3 -5
- package/src/react/components/badge.tsx +3 -3
- package/src/react/components/item.tsx +2 -2
- package/src/react/components/text.tsx +5 -17
- package/src/react-native/components/snap-action-button.tsx +7 -13
- package/src/react-native/components/snap-badge.tsx +5 -3
- package/src/react-native/components/snap-item.tsx +1 -6
- package/src/react-native/components/snap-text.tsx +0 -2
- package/src/ui/badge.ts +2 -0
- package/src/ui/button.ts +1 -1
- package/src/ui/catalog.ts +2 -2
- package/src/ui/image.ts +1 -1
- package/src/ui/item.ts +1 -1
- package/src/ui/text.ts +2 -2
|
@@ -5,14 +5,12 @@ import { cn } from "@neynar/ui/utils";
|
|
|
5
5
|
import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
|
|
6
6
|
import { ICON_MAP } from "./icon.js";
|
|
7
7
|
const VARIANT_MAP = {
|
|
8
|
-
|
|
8
|
+
primary: "default",
|
|
9
9
|
secondary: "secondary",
|
|
10
|
-
outline: "outline",
|
|
11
|
-
ghost: "ghost",
|
|
12
10
|
};
|
|
13
11
|
export function SnapActionButton({ element: { props }, emit, }) {
|
|
14
12
|
const label = String(props.label ?? "Action");
|
|
15
|
-
const variant = VARIANT_MAP[String(props.variant ?? "
|
|
13
|
+
const variant = VARIANT_MAP[String(props.variant ?? "secondary")] ?? "secondary";
|
|
16
14
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
17
15
|
const accentStyle = useSnapAccentScopeStyle();
|
|
18
16
|
const Icon = iconName ? ICON_MAP[iconName] : undefined;
|
|
@@ -5,14 +5,13 @@ import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
|
|
|
5
5
|
import { ICON_MAP } from "./icon.js";
|
|
6
6
|
export function SnapBadge({ element: { props }, }) {
|
|
7
7
|
const content = String(props.label ?? "");
|
|
8
|
+
const variant = String(props.variant ?? "default");
|
|
8
9
|
const color = props.color ? String(props.color) : undefined;
|
|
9
10
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
10
11
|
const accentStyle = useSnapAccentScopeStyle();
|
|
11
12
|
const isAccent = !color || color === "accent";
|
|
12
13
|
const Icon = iconName ? ICON_MAP[iconName] : undefined;
|
|
13
|
-
return (_jsx("span", { style: isAccent ? accentStyle : undefined, children: _jsxs(Badge, { variant:
|
|
14
|
-
// TODO: fix outline badge border color in @neynar/ui — too bright in dark mode
|
|
15
|
-
style: !isAccent
|
|
14
|
+
return (_jsx("span", { style: isAccent ? accentStyle : undefined, children: _jsxs(Badge, { variant: variant, className: "gap-1", style: variant === "outline" && !isAccent
|
|
16
15
|
? { borderColor: `var(--snap-color-${color})`, color: `var(--snap-color-${color})` }
|
|
17
16
|
: undefined, children: [Icon && _jsx(Icon, { size: 12 }), content] }) }));
|
|
18
17
|
}
|
|
@@ -5,5 +5,5 @@ export function SnapItem({ element: { props }, children, }) {
|
|
|
5
5
|
const title = String(props.title ?? "");
|
|
6
6
|
const description = props.description ? String(props.description) : undefined;
|
|
7
7
|
const variant = props.variant ?? "default";
|
|
8
|
-
return (_jsxs(Item, { variant: variant, className:
|
|
8
|
+
return (_jsxs(Item, { variant: variant, className: "flex-1 py-1.5 px-2.5", children: [_jsxs(ItemContent, { className: "gap-0.5", children: [_jsx(ItemTitle, { children: title }), description && _jsx(ItemDescription, { className: "mt-0", children: description })] }), children && _jsx(ItemActions, { children: children })] }));
|
|
9
9
|
}
|
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
-
import { Text
|
|
3
|
+
import { Text } from "@neynar/ui/typography";
|
|
4
4
|
const SIZE_MAP = {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
sm: { component: "text", textSize: "sm", order: undefined },
|
|
5
|
+
md: { component: "text", textSize: "base" },
|
|
6
|
+
sm: { component: "text", textSize: "sm" },
|
|
8
7
|
};
|
|
9
8
|
const WEIGHT_MAP = {
|
|
10
9
|
bold: "bold",
|
|
11
|
-
medium: "medium",
|
|
12
10
|
normal: "normal",
|
|
13
11
|
};
|
|
14
12
|
export function SnapText({ element: { props }, }) {
|
|
@@ -17,9 +15,5 @@ export function SnapText({ element: { props }, }) {
|
|
|
17
15
|
const weight = props.weight ? String(props.weight) : undefined;
|
|
18
16
|
const align = props.align ?? undefined;
|
|
19
17
|
const config = SIZE_MAP[size] ?? SIZE_MAP.md;
|
|
20
|
-
const alignClass = align === "center" ? "text-center" : align === "right" ? "text-right" : "";
|
|
21
|
-
if (config.component === "title") {
|
|
22
|
-
return (_jsx(Title, { order: config.order, weight: weight ?? "bold", className: `flex-1 ${alignClass}`, children: content }));
|
|
23
|
-
}
|
|
24
18
|
return (_jsx(Text, { size: config.textSize, weight: weight, align: align, className: "flex-1", children: content }));
|
|
25
19
|
}
|
|
@@ -4,34 +4,28 @@ import { useSnapPalette } from "../use-snap-palette.js";
|
|
|
4
4
|
import { useSnapTheme } from "../theme.js";
|
|
5
5
|
import { ICON_MAP } from "./snap-icon.js";
|
|
6
6
|
const VARIANT_MAP = {
|
|
7
|
-
|
|
7
|
+
primary: "primary",
|
|
8
8
|
secondary: "secondary",
|
|
9
|
-
outline: "outline",
|
|
10
|
-
ghost: "ghost",
|
|
11
9
|
};
|
|
12
10
|
export function SnapActionButton({ element: { props }, emit, }) {
|
|
13
11
|
const { accentHex } = useSnapPalette();
|
|
14
12
|
const { colors } = useSnapTheme();
|
|
15
13
|
const label = String(props.label ?? "Action");
|
|
16
|
-
const variant = VARIANT_MAP[String(props.variant ?? "
|
|
14
|
+
const variant = VARIANT_MAP[String(props.variant ?? "secondary")] ?? "secondary";
|
|
17
15
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
18
16
|
const variantStyle = (() => {
|
|
19
17
|
switch (variant) {
|
|
20
|
-
case "
|
|
18
|
+
case "primary":
|
|
21
19
|
return { backgroundColor: accentHex };
|
|
22
20
|
case "secondary":
|
|
23
21
|
return { backgroundColor: "transparent", borderWidth: 1.5, borderColor: accentHex };
|
|
24
|
-
case "outline":
|
|
25
|
-
return { backgroundColor: "rgba(255,255,255,0.04)", borderWidth: 1, borderColor: colors.border };
|
|
26
|
-
case "ghost":
|
|
27
|
-
return { backgroundColor: "transparent" };
|
|
28
22
|
}
|
|
29
23
|
})();
|
|
30
|
-
const textColor = variant === "
|
|
31
|
-
const iconColor = variant === "
|
|
24
|
+
const textColor = variant === "primary" ? "#fff" : accentHex;
|
|
25
|
+
const iconColor = variant === "primary" ? "#fff" : accentHex;
|
|
32
26
|
return (_jsx(View, { style: styles.outer, children: _jsxs(Pressable, { style: ({ pressed }) => [
|
|
33
27
|
styles.btn,
|
|
34
|
-
variant === "
|
|
28
|
+
variant === "primary" ? styles.btnDefault : styles.btnOther,
|
|
35
29
|
variantStyle,
|
|
36
30
|
pressed && styles.pressed,
|
|
37
31
|
], onPress: () => {
|
|
@@ -5,19 +5,21 @@ import { ICON_MAP } from "./snap-icon.js";
|
|
|
5
5
|
export function SnapBadge({ element: { props }, }) {
|
|
6
6
|
const { accentHex, hex } = useSnapPalette();
|
|
7
7
|
const label = String(props.label ?? "");
|
|
8
|
+
const variant = String(props.variant ?? "default");
|
|
8
9
|
const color = props.color ? String(props.color) : undefined;
|
|
9
10
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
10
11
|
const isAccent = !color || color === "accent";
|
|
11
12
|
const resolvedColor = isAccent ? accentHex : hex(color);
|
|
13
|
+
const isFilled = variant === "default";
|
|
12
14
|
const Icon = iconName ? ICON_MAP[iconName] : undefined;
|
|
13
15
|
return (_jsxs(View, { style: [
|
|
14
16
|
styles.badge,
|
|
15
|
-
|
|
17
|
+
isFilled
|
|
16
18
|
? { backgroundColor: resolvedColor, borderColor: resolvedColor }
|
|
17
19
|
: { borderColor: resolvedColor },
|
|
18
|
-
], children: [Icon && (_jsx(Icon, { size: 12, color:
|
|
20
|
+
], children: [Icon && (_jsx(Icon, { size: 12, color: isFilled ? "#fff" : resolvedColor })), _jsx(Text, { style: [
|
|
19
21
|
styles.label,
|
|
20
|
-
{ color:
|
|
22
|
+
{ color: isFilled ? "#fff" : resolvedColor },
|
|
21
23
|
], children: label })] }));
|
|
22
24
|
}
|
|
23
25
|
const styles = StyleSheet.create({
|
|
@@ -8,11 +8,7 @@ export function SnapItem({ element: { props }, children, }) {
|
|
|
8
8
|
? String(props.description)
|
|
9
9
|
: undefined;
|
|
10
10
|
const variant = String(props.variant ?? "default");
|
|
11
|
-
const containerVariant =
|
|
12
|
-
? { borderWidth: 1, borderColor: colors.border + "80", borderRadius: 8, padding: 10 }
|
|
13
|
-
: variant === "muted"
|
|
14
|
-
? { backgroundColor: "rgba(255,255,255,0.04)", borderRadius: 8, padding: 10 }
|
|
15
|
-
: { paddingVertical: 8, paddingHorizontal: 10 };
|
|
11
|
+
const containerVariant = { paddingVertical: 8, paddingHorizontal: 10 };
|
|
16
12
|
return (_jsxs(View, { style: [styles.container, containerVariant], children: [_jsxs(View, { style: styles.content, children: [_jsx(Text, { style: [styles.title, { color: colors.text }], children: title }), description ? (_jsx(Text, { style: [styles.description, { color: colors.textSecondary }], children: description })) : null] }), children ? (_jsx(View, { style: styles.actions, children: _jsx(View, { style: { flex: 0 }, children: children }) })) : null] }));
|
|
17
13
|
}
|
|
18
14
|
const styles = StyleSheet.create({
|
|
@@ -2,13 +2,11 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { StyleSheet, Text, View } from "react-native";
|
|
3
3
|
import { useSnapTheme } from "../theme.js";
|
|
4
4
|
const SIZE_STYLES = {
|
|
5
|
-
lg: { fontSize: 20, fontWeight: "700" },
|
|
6
5
|
md: { fontSize: 16, lineHeight: 24 },
|
|
7
6
|
sm: { fontSize: 13 },
|
|
8
7
|
};
|
|
9
8
|
const WEIGHT_MAP = {
|
|
10
9
|
bold: "700",
|
|
11
|
-
medium: "500",
|
|
12
10
|
normal: "400",
|
|
13
11
|
};
|
|
14
12
|
export function SnapText({ element: { props }, }) {
|
package/dist/ui/badge.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
export declare const BADGE_VARIANTS: readonly ["default", "outline"];
|
|
2
3
|
export declare const BADGE_MAX_LABEL_CHARS = 30;
|
|
3
4
|
export declare const badgeProps: z.ZodObject<{
|
|
4
5
|
label: z.ZodString;
|
|
6
|
+
variant: z.ZodOptional<z.ZodEnum<{
|
|
7
|
+
default: "default";
|
|
8
|
+
outline: "outline";
|
|
9
|
+
}>>;
|
|
5
10
|
color: z.ZodOptional<z.ZodEnum<{
|
|
6
11
|
gray: "gray";
|
|
7
12
|
blue: "blue";
|
package/dist/ui/badge.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { PROGRESS_COLOR_VALUES } from "../colors.js";
|
|
3
3
|
import { ICON_NAMES } from "./icon.js";
|
|
4
|
+
export const BADGE_VARIANTS = ["default", "outline"];
|
|
4
5
|
export const BADGE_MAX_LABEL_CHARS = 30;
|
|
5
6
|
export const badgeProps = z.object({
|
|
6
7
|
label: z.string().min(1).max(BADGE_MAX_LABEL_CHARS),
|
|
8
|
+
variant: z.enum(BADGE_VARIANTS).optional(),
|
|
7
9
|
color: z.enum(PROGRESS_COLOR_VALUES).optional(),
|
|
8
10
|
icon: z.enum(ICON_NAMES).optional(),
|
|
9
11
|
});
|
package/dist/ui/button.d.ts
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const BUTTON_VARIANTS: readonly ["
|
|
2
|
+
export declare const BUTTON_VARIANTS: readonly ["secondary", "primary"];
|
|
3
3
|
export declare const BUTTON_MAX_LABEL_CHARS = 30;
|
|
4
4
|
export declare const buttonProps: z.ZodObject<{
|
|
5
5
|
label: z.ZodString;
|
|
6
6
|
variant: z.ZodOptional<z.ZodEnum<{
|
|
7
|
-
default: "default";
|
|
8
7
|
secondary: "secondary";
|
|
9
|
-
|
|
10
|
-
ghost: "ghost";
|
|
8
|
+
primary: "primary";
|
|
11
9
|
}>>;
|
|
12
10
|
icon: z.ZodOptional<z.ZodEnum<{
|
|
13
11
|
"arrow-right": "arrow-right";
|
package/dist/ui/button.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { ICON_NAMES } from "./icon.js";
|
|
3
|
-
export const BUTTON_VARIANTS = ["
|
|
3
|
+
export const BUTTON_VARIANTS = ["secondary", "primary"];
|
|
4
4
|
export const BUTTON_MAX_LABEL_CHARS = 30;
|
|
5
5
|
export const buttonProps = z.object({
|
|
6
6
|
label: z.string().min(1).max(BUTTON_MAX_LABEL_CHARS),
|
package/dist/ui/catalog.d.ts
CHANGED
|
@@ -37,6 +37,10 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
37
37
|
badge: {
|
|
38
38
|
props: z.ZodObject<{
|
|
39
39
|
label: z.ZodString;
|
|
40
|
+
variant: z.ZodOptional<z.ZodEnum<{
|
|
41
|
+
default: "default";
|
|
42
|
+
outline: "outline";
|
|
43
|
+
}>>;
|
|
40
44
|
color: z.ZodOptional<z.ZodEnum<{
|
|
41
45
|
gray: "gray";
|
|
42
46
|
blue: "blue";
|
|
@@ -90,10 +94,8 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
90
94
|
props: z.ZodObject<{
|
|
91
95
|
label: z.ZodString;
|
|
92
96
|
variant: z.ZodOptional<z.ZodEnum<{
|
|
93
|
-
default: "default";
|
|
94
97
|
secondary: "secondary";
|
|
95
|
-
|
|
96
|
-
ghost: "ghost";
|
|
98
|
+
primary: "primary";
|
|
97
99
|
}>>;
|
|
98
100
|
icon: z.ZodOptional<z.ZodEnum<{
|
|
99
101
|
"arrow-right": "arrow-right";
|
|
@@ -179,8 +181,6 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
179
181
|
description: z.ZodOptional<z.ZodString>;
|
|
180
182
|
variant: z.ZodOptional<z.ZodEnum<{
|
|
181
183
|
default: "default";
|
|
182
|
-
outline: "outline";
|
|
183
|
-
muted: "muted";
|
|
184
184
|
}>>;
|
|
185
185
|
}, z.core.$strip>;
|
|
186
186
|
description: string;
|
|
@@ -260,7 +260,6 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
260
260
|
"1:1": "1:1";
|
|
261
261
|
"16:9": "16:9";
|
|
262
262
|
"4:3": "4:3";
|
|
263
|
-
"3:4": "3:4";
|
|
264
263
|
"9:16": "9:16";
|
|
265
264
|
}>;
|
|
266
265
|
alt: z.ZodOptional<z.ZodString>;
|
|
@@ -323,11 +322,9 @@ export declare const snapJsonRenderCatalog: import("@json-render/core").Catalog<
|
|
|
323
322
|
size: z.ZodOptional<z.ZodEnum<{
|
|
324
323
|
sm: "sm";
|
|
325
324
|
md: "md";
|
|
326
|
-
lg: "lg";
|
|
327
325
|
}>>;
|
|
328
326
|
weight: z.ZodOptional<z.ZodEnum<{
|
|
329
327
|
bold: "bold";
|
|
330
|
-
medium: "medium";
|
|
331
328
|
normal: "normal";
|
|
332
329
|
}>>;
|
|
333
330
|
align: z.ZodOptional<z.ZodEnum<{
|
package/dist/ui/catalog.js
CHANGED
|
@@ -28,7 +28,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
28
28
|
components: {
|
|
29
29
|
badge: {
|
|
30
30
|
props: badgeProps,
|
|
31
|
-
description: "Inline label — variant: default
|
|
31
|
+
description: "Inline label — variant: default (filled) or outline (bordered). Optional color and icon.",
|
|
32
32
|
},
|
|
33
33
|
button: {
|
|
34
34
|
props: buttonProps,
|
|
@@ -80,7 +80,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
80
80
|
},
|
|
81
81
|
text: {
|
|
82
82
|
props: textProps,
|
|
83
|
-
description: "Text block — size:
|
|
83
|
+
description: "Text block — size: md (body, default), sm (caption). Optional weight and align.",
|
|
84
84
|
},
|
|
85
85
|
},
|
|
86
86
|
actions: {
|
package/dist/ui/image.d.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const IMAGE_ASPECTS: readonly ["1:1", "16:9", "4:3", "
|
|
2
|
+
export declare const IMAGE_ASPECTS: readonly ["1:1", "16:9", "4:3", "9:16"];
|
|
3
3
|
export declare const imageProps: z.ZodObject<{
|
|
4
4
|
url: z.ZodString;
|
|
5
5
|
aspect: z.ZodEnum<{
|
|
6
6
|
"1:1": "1:1";
|
|
7
7
|
"16:9": "16:9";
|
|
8
8
|
"4:3": "4:3";
|
|
9
|
-
"3:4": "3:4";
|
|
10
9
|
"9:16": "9:16";
|
|
11
10
|
}>;
|
|
12
11
|
alt: z.ZodOptional<z.ZodString>;
|
package/dist/ui/image.js
CHANGED
package/dist/ui/item.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const ITEM_VARIANTS: readonly ["default"
|
|
2
|
+
export declare const ITEM_VARIANTS: readonly ["default"];
|
|
3
3
|
export declare const ITEM_MAX_TITLE_CHARS = 100;
|
|
4
4
|
export declare const ITEM_MAX_DESCRIPTION_CHARS = 160;
|
|
5
5
|
export declare const itemProps: z.ZodObject<{
|
|
@@ -7,8 +7,6 @@ export declare const itemProps: z.ZodObject<{
|
|
|
7
7
|
description: z.ZodOptional<z.ZodString>;
|
|
8
8
|
variant: z.ZodOptional<z.ZodEnum<{
|
|
9
9
|
default: "default";
|
|
10
|
-
outline: "outline";
|
|
11
|
-
muted: "muted";
|
|
12
10
|
}>>;
|
|
13
11
|
}, z.core.$strip>;
|
|
14
12
|
export type ItemProps = z.infer<typeof itemProps>;
|
package/dist/ui/item.js
CHANGED
package/dist/ui/text.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export declare const TEXT_SIZES: readonly ["
|
|
3
|
-
export declare const TEXT_WEIGHTS: readonly ["bold", "
|
|
2
|
+
export declare const TEXT_SIZES: readonly ["md", "sm"];
|
|
3
|
+
export declare const TEXT_WEIGHTS: readonly ["bold", "normal"];
|
|
4
4
|
export declare const TEXT_ALIGNS: readonly ["left", "center", "right"];
|
|
5
5
|
export declare const TEXT_MAX_CONTENT_CHARS = 320;
|
|
6
6
|
export declare const textProps: z.ZodObject<{
|
|
@@ -8,11 +8,9 @@ export declare const textProps: z.ZodObject<{
|
|
|
8
8
|
size: z.ZodOptional<z.ZodEnum<{
|
|
9
9
|
sm: "sm";
|
|
10
10
|
md: "md";
|
|
11
|
-
lg: "lg";
|
|
12
11
|
}>>;
|
|
13
12
|
weight: z.ZodOptional<z.ZodEnum<{
|
|
14
13
|
bold: "bold";
|
|
15
|
-
medium: "medium";
|
|
16
14
|
normal: "normal";
|
|
17
15
|
}>>;
|
|
18
16
|
align: z.ZodOptional<z.ZodEnum<{
|
package/dist/ui/text.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
export const TEXT_SIZES = ["
|
|
3
|
-
export const TEXT_WEIGHTS = ["bold", "
|
|
2
|
+
export const TEXT_SIZES = ["md", "sm"];
|
|
3
|
+
export const TEXT_WEIGHTS = ["bold", "normal"];
|
|
4
4
|
export const TEXT_ALIGNS = ["left", "center", "right"];
|
|
5
5
|
export const TEXT_MAX_CONTENT_CHARS = 320;
|
|
6
6
|
export const textProps = z.object({
|
package/llms.txt
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
# @farcaster/snap
|
|
2
|
+
|
|
3
|
+
> TypeScript SDK for building Farcaster Snaps — interactive feed cards driven by server-returned JSON. Provides schema validation, component catalog, React + React Native renderers, and server utilities.
|
|
4
|
+
|
|
5
|
+
## SnapResponse Format
|
|
6
|
+
|
|
7
|
+
Every snap handler returns a `SnapResponse`:
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"version": "1.0",
|
|
12
|
+
"theme": { "accent": "purple" },
|
|
13
|
+
"effects": ["confetti"],
|
|
14
|
+
"ui": {
|
|
15
|
+
"root": "page",
|
|
16
|
+
"elements": {
|
|
17
|
+
"page": { "type": "stack", "props": {}, "children": ["title", "btn"] },
|
|
18
|
+
"title": { "type": "text", "props": { "content": "Hello", "weight": "bold" } },
|
|
19
|
+
"btn": {
|
|
20
|
+
"type": "button",
|
|
21
|
+
"props": { "label": "Go", "variant": "primary" },
|
|
22
|
+
"on": { "press": { "action": "submit", "params": { "target": "https://example.com/" } } }
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Top-level fields: `version` (required, `"1.0"`), `theme` (optional, `{ accent: PaletteColor }`), `effects` (optional, `["confetti"]`), `ui` (required).
|
|
30
|
+
|
|
31
|
+
`ui.root` is the ID of the root element. `ui.elements` is a flat map of element ID to element definition.
|
|
32
|
+
|
|
33
|
+
## Components (14 total)
|
|
34
|
+
|
|
35
|
+
### Display Components
|
|
36
|
+
|
|
37
|
+
**badge** — Inline label with optional icon.
|
|
38
|
+
- `label` (string, required, max 30)
|
|
39
|
+
- `variant` (optional): `"default"` (filled) | `"outline"` (bordered). Default: `"default"`
|
|
40
|
+
- `color` (optional): PaletteColor. Default: `"accent"`
|
|
41
|
+
- `icon` (optional): IconName
|
|
42
|
+
|
|
43
|
+
**button** — Action trigger. Bind via `on.press`.
|
|
44
|
+
- `label` (string, required, max 30)
|
|
45
|
+
- `variant` (optional): `"primary"` (filled accent) | `"secondary"` (bordered). Default: `"secondary"`
|
|
46
|
+
- `icon` (optional): IconName
|
|
47
|
+
|
|
48
|
+
**icon** — Standalone Lucide icon.
|
|
49
|
+
- `name` (IconName, required)
|
|
50
|
+
- `color` (optional): PaletteColor. Default: `"accent"`
|
|
51
|
+
- `size` (optional): `"sm"` (16px) | `"md"` (20px). Default: `"md"`
|
|
52
|
+
|
|
53
|
+
**image** — HTTPS image with fixed aspect ratio.
|
|
54
|
+
- `url` (string, required)
|
|
55
|
+
- `aspect` (required): `"1:1"` | `"16:9"` | `"4:3"` | `"9:16"`
|
|
56
|
+
- `alt` (string, optional)
|
|
57
|
+
|
|
58
|
+
**item** — Content row with title and right-side actions slot.
|
|
59
|
+
- `title` (string, required, max 100)
|
|
60
|
+
- `description` (string, optional, max 160)
|
|
61
|
+
- `variant` (optional): `"default"`. Default: `"default"`
|
|
62
|
+
- Children render in the actions slot (right side)
|
|
63
|
+
|
|
64
|
+
**progress** — Horizontal progress bar.
|
|
65
|
+
- `value` (number, required, 0 to max)
|
|
66
|
+
- `max` (number, required, > 0)
|
|
67
|
+
- `label` (string, optional, max 60)
|
|
68
|
+
|
|
69
|
+
**separator** — Visual divider.
|
|
70
|
+
- `orientation` (optional): `"horizontal"` | `"vertical"`. Default: `"horizontal"`
|
|
71
|
+
|
|
72
|
+
**text** — Text block.
|
|
73
|
+
- `content` (string, required, max 320)
|
|
74
|
+
- `size` (optional): `"md"` (body) | `"sm"` (caption). Default: `"md"`
|
|
75
|
+
- `weight` (optional): `"bold"` | `"normal"`. Default: `"normal"`
|
|
76
|
+
- `align` (optional): `"left"` | `"center"` | `"right"`. Default: `"left"`
|
|
77
|
+
|
|
78
|
+
### Container Components
|
|
79
|
+
|
|
80
|
+
**stack** — Layout container.
|
|
81
|
+
- `direction` (optional): `"vertical"` | `"horizontal"`. Default: `"vertical"`
|
|
82
|
+
- `gap` (optional): `"none"` | `"sm"` | `"md"` | `"lg"`. Default: `"md"`
|
|
83
|
+
- `justify` (optional): `"start"` | `"center"` | `"end"` | `"between"` | `"around"`
|
|
84
|
+
- Children are element IDs
|
|
85
|
+
|
|
86
|
+
**item_group** — Groups item children.
|
|
87
|
+
- `border` (boolean, optional)
|
|
88
|
+
- `separator` (boolean, optional)
|
|
89
|
+
- `gap` (optional): `"none"` | `"sm"` | `"md"` | `"lg"`
|
|
90
|
+
- Children must be item elements
|
|
91
|
+
|
|
92
|
+
### Field Components
|
|
93
|
+
|
|
94
|
+
Field values are sent in POST `inputs[name]` when a `submit` action fires.
|
|
95
|
+
|
|
96
|
+
**input** — Text or number input.
|
|
97
|
+
- `name` (string, required)
|
|
98
|
+
- `type` (optional): `"text"` | `"number"`. Default: `"text"`
|
|
99
|
+
- `label` (string, optional, max 60)
|
|
100
|
+
- `placeholder` (string, optional, max 60)
|
|
101
|
+
- `defaultValue` (string, optional)
|
|
102
|
+
- `maxLength` (number, optional, 1-280)
|
|
103
|
+
- POST value: string
|
|
104
|
+
|
|
105
|
+
**slider** — Numeric range.
|
|
106
|
+
- `name` (string, required)
|
|
107
|
+
- `min` (number, required)
|
|
108
|
+
- `max` (number, required, >= min)
|
|
109
|
+
- `step` (number, optional, > 0. Default: 1)
|
|
110
|
+
- `defaultValue` (number, optional, between min and max)
|
|
111
|
+
- `label` (string, optional, max 60)
|
|
112
|
+
- POST value: number
|
|
113
|
+
|
|
114
|
+
**switch** — Boolean toggle.
|
|
115
|
+
- `name` (string, required)
|
|
116
|
+
- `label` (string, optional, max 60)
|
|
117
|
+
- `defaultChecked` (boolean, optional)
|
|
118
|
+
- POST value: boolean
|
|
119
|
+
|
|
120
|
+
**toggle_group** — Single or multi-select choice group.
|
|
121
|
+
- `name` (string, required)
|
|
122
|
+
- `options` (string[], required, 2-6 items, each max 30 chars)
|
|
123
|
+
- `multiple` (boolean, optional)
|
|
124
|
+
- `orientation` (optional): `"horizontal"` | `"vertical"`. Default: `"horizontal"`
|
|
125
|
+
- `defaultValue` (string | string[], optional)
|
|
126
|
+
- `variant` (optional): `"default"` | `"outline"`. Default: `"default"`
|
|
127
|
+
- `label` (string, optional, max 60)
|
|
128
|
+
- POST value: string (single) or string[] (multiple)
|
|
129
|
+
|
|
130
|
+
## Actions (9 types)
|
|
131
|
+
|
|
132
|
+
Bound to buttons via `on.press`:
|
|
133
|
+
|
|
134
|
+
| Action | Params | Description |
|
|
135
|
+
|--------|--------|-------------|
|
|
136
|
+
| `submit` | `target` (URL) | POST to server, get next page |
|
|
137
|
+
| `open_url` | `target` (URL) | Open in system browser |
|
|
138
|
+
| `open_mini_app` | `target` (URL) | Open as Farcaster mini app |
|
|
139
|
+
| `view_cast` | `hash` (string) | Navigate to a cast |
|
|
140
|
+
| `view_profile` | `fid` (number) | Navigate to a profile |
|
|
141
|
+
| `compose_cast` | `text?`, `channelKey?`, `embeds?` | Open cast composer |
|
|
142
|
+
| `view_token` | `token` (CAIP-19) | View token in wallet |
|
|
143
|
+
| `send_token` | `token`, `amount?`, `recipientFid?`, `recipientAddress?` | Send token flow |
|
|
144
|
+
| `swap_token` | `sellToken?`, `buyToken?` | Swap token flow |
|
|
145
|
+
|
|
146
|
+
## Icon Names (34)
|
|
147
|
+
|
|
148
|
+
`arrow-right`, `arrow-left`, `external-link`, `chevron-right`, `check`, `x`, `alert-triangle`, `info`, `clock`, `heart`, `message-circle`, `repeat`, `share`, `user`, `users`, `star`, `trophy`, `zap`, `flame`, `gift`, `image`, `play`, `pause`, `wallet`, `coins`, `plus`, `minus`, `refresh-cw`, `bookmark`, `thumbs-up`, `thumbs-down`, `trending-up`, `trending-down`
|
|
149
|
+
|
|
150
|
+
## Color Palette
|
|
151
|
+
|
|
152
|
+
`gray`, `blue`, `red`, `amber`, `green`, `teal`, `purple`, `pink`
|
|
153
|
+
|
|
154
|
+
Plus the special value `"accent"` which references `theme.accent`.
|
|
155
|
+
|
|
156
|
+
## Package Exports
|
|
157
|
+
|
|
158
|
+
```ts
|
|
159
|
+
import { snapJsonRenderCatalog } from "@farcaster/snap/ui";
|
|
160
|
+
import { parseRequest, verifyJFSRequestBody } from "@farcaster/snap/server";
|
|
161
|
+
import { createInMemoryDataStore } from "@farcaster/snap";
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
- `@farcaster/snap` — schemas, types, validation, data store
|
|
165
|
+
- `@farcaster/snap/ui` — json-render catalog, component schemas
|
|
166
|
+
- `@farcaster/snap/server` — request parsing, JFS verification
|
|
167
|
+
- `@farcaster/snap-hono` — Hono adapter (`registerSnapHandler`)
|
|
168
|
+
- `@farcaster/snap-turso` — Turso data store middleware
|
|
169
|
+
|
|
170
|
+
## Full Documentation
|
|
171
|
+
|
|
172
|
+
https://docs.farcaster.xyz/snap
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@farcaster/snap",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Farcaster Snaps 🫰",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -109,7 +109,8 @@
|
|
|
109
109
|
},
|
|
110
110
|
"files": [
|
|
111
111
|
"dist",
|
|
112
|
-
"src"
|
|
112
|
+
"src",
|
|
113
|
+
"llms.txt"
|
|
113
114
|
],
|
|
114
115
|
"publishConfig": {
|
|
115
116
|
"access": "public"
|
|
@@ -5,11 +5,9 @@ import { cn } from "@neynar/ui/utils";
|
|
|
5
5
|
import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent";
|
|
6
6
|
import { ICON_MAP } from "./icon";
|
|
7
7
|
|
|
8
|
-
const VARIANT_MAP: Record<string, "default" | "
|
|
9
|
-
|
|
8
|
+
const VARIANT_MAP: Record<string, "default" | "secondary"> = {
|
|
9
|
+
primary: "default",
|
|
10
10
|
secondary: "secondary",
|
|
11
|
-
outline: "outline",
|
|
12
|
-
ghost: "ghost",
|
|
13
11
|
};
|
|
14
12
|
|
|
15
13
|
export function SnapActionButton({
|
|
@@ -20,7 +18,7 @@ export function SnapActionButton({
|
|
|
20
18
|
emit: (name: string) => void;
|
|
21
19
|
}) {
|
|
22
20
|
const label = String(props.label ?? "Action");
|
|
23
|
-
const variant = VARIANT_MAP[String(props.variant ?? "
|
|
21
|
+
const variant = VARIANT_MAP[String(props.variant ?? "secondary")] ?? "secondary";
|
|
24
22
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
25
23
|
const accentStyle = useSnapAccentScopeStyle();
|
|
26
24
|
|
|
@@ -10,6 +10,7 @@ export function SnapBadge({
|
|
|
10
10
|
element: { props: Record<string, unknown> };
|
|
11
11
|
}) {
|
|
12
12
|
const content = String(props.label ?? "");
|
|
13
|
+
const variant = String(props.variant ?? "default") as "default" | "outline";
|
|
13
14
|
const color = props.color ? String(props.color) : undefined;
|
|
14
15
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
15
16
|
const accentStyle = useSnapAccentScopeStyle();
|
|
@@ -20,11 +21,10 @@ export function SnapBadge({
|
|
|
20
21
|
return (
|
|
21
22
|
<span style={isAccent ? accentStyle : undefined}>
|
|
22
23
|
<Badge
|
|
23
|
-
variant={
|
|
24
|
+
variant={variant}
|
|
24
25
|
className="gap-1"
|
|
25
|
-
// TODO: fix outline badge border color in @neynar/ui — too bright in dark mode
|
|
26
26
|
style={
|
|
27
|
-
!isAccent
|
|
27
|
+
variant === "outline" && !isAccent
|
|
28
28
|
? { borderColor: `var(--snap-color-${color})`, color: `var(--snap-color-${color})` }
|
|
29
29
|
: undefined
|
|
30
30
|
}
|
|
@@ -19,10 +19,10 @@ export function SnapItem({
|
|
|
19
19
|
const title = String(props.title ?? "");
|
|
20
20
|
const description = props.description ? String(props.description) : undefined;
|
|
21
21
|
const variant =
|
|
22
|
-
(props.variant as "default"
|
|
22
|
+
(props.variant as "default") ?? "default";
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
|
-
<Item variant={variant} className=
|
|
25
|
+
<Item variant={variant} className="flex-1 py-1.5 px-2.5">
|
|
26
26
|
<ItemContent className="gap-0.5">
|
|
27
27
|
<ItemTitle>{title}</ItemTitle>
|
|
28
28
|
{description && <ItemDescription className="mt-0">{description}</ItemDescription>}
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Text
|
|
3
|
+
import { Text } from "@neynar/ui/typography";
|
|
4
4
|
|
|
5
5
|
const SIZE_MAP = {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
sm: { component: "text", textSize: "sm" as const, order: undefined },
|
|
6
|
+
md: { component: "text", textSize: "base" as const },
|
|
7
|
+
sm: { component: "text", textSize: "sm" as const },
|
|
9
8
|
} as const;
|
|
10
9
|
|
|
11
10
|
const WEIGHT_MAP = {
|
|
12
11
|
bold: "bold",
|
|
13
|
-
medium: "medium",
|
|
14
12
|
normal: "normal",
|
|
15
13
|
} as const;
|
|
16
14
|
|
|
@@ -20,21 +18,11 @@ export function SnapText({
|
|
|
20
18
|
element: { props: Record<string, unknown> };
|
|
21
19
|
}) {
|
|
22
20
|
const content = String(props.content ?? "");
|
|
23
|
-
const size = String(props.size ?? "md") as "
|
|
24
|
-
const weight = props.weight ? String(props.weight) as "bold" | "
|
|
21
|
+
const size = String(props.size ?? "md") as "md" | "sm";
|
|
22
|
+
const weight = props.weight ? String(props.weight) as "bold" | "normal" : undefined;
|
|
25
23
|
const align = (props.align as "left" | "center" | "right") ?? undefined;
|
|
26
24
|
const config = SIZE_MAP[size] ?? SIZE_MAP.md;
|
|
27
25
|
|
|
28
|
-
const alignClass = align === "center" ? "text-center" : align === "right" ? "text-right" : "";
|
|
29
|
-
|
|
30
|
-
if (config.component === "title") {
|
|
31
|
-
return (
|
|
32
|
-
<Title order={config.order} weight={weight ?? "bold"} className={`flex-1 ${alignClass}`}>
|
|
33
|
-
{content}
|
|
34
|
-
</Title>
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
26
|
return (
|
|
39
27
|
<Text size={config.textSize} weight={weight} align={align} className="flex-1">
|
|
40
28
|
{content}
|
|
@@ -6,11 +6,9 @@ import { useSnapPalette } from "../use-snap-palette";
|
|
|
6
6
|
import { useSnapTheme } from "../theme";
|
|
7
7
|
import { ICON_MAP } from "./snap-icon";
|
|
8
8
|
|
|
9
|
-
const VARIANT_MAP: Record<string, "
|
|
10
|
-
|
|
9
|
+
const VARIANT_MAP: Record<string, "primary" | "secondary"> = {
|
|
10
|
+
primary: "primary",
|
|
11
11
|
secondary: "secondary",
|
|
12
|
-
outline: "outline",
|
|
13
|
-
ghost: "ghost",
|
|
14
12
|
};
|
|
15
13
|
|
|
16
14
|
export function SnapActionButton({
|
|
@@ -20,31 +18,27 @@ export function SnapActionButton({
|
|
|
20
18
|
const { accentHex } = useSnapPalette();
|
|
21
19
|
const { colors } = useSnapTheme();
|
|
22
20
|
const label = String(props.label ?? "Action");
|
|
23
|
-
const variant = VARIANT_MAP[String(props.variant ?? "
|
|
21
|
+
const variant = VARIANT_MAP[String(props.variant ?? "secondary")] ?? "secondary";
|
|
24
22
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
25
23
|
|
|
26
24
|
const variantStyle = (() => {
|
|
27
25
|
switch (variant) {
|
|
28
|
-
case "
|
|
26
|
+
case "primary":
|
|
29
27
|
return { backgroundColor: accentHex };
|
|
30
28
|
case "secondary":
|
|
31
29
|
return { backgroundColor: "transparent", borderWidth: 1.5, borderColor: accentHex };
|
|
32
|
-
case "outline":
|
|
33
|
-
return { backgroundColor: "rgba(255,255,255,0.04)", borderWidth: 1, borderColor: colors.border };
|
|
34
|
-
case "ghost":
|
|
35
|
-
return { backgroundColor: "transparent" };
|
|
36
30
|
}
|
|
37
31
|
})();
|
|
38
32
|
|
|
39
|
-
const textColor = variant === "
|
|
40
|
-
const iconColor = variant === "
|
|
33
|
+
const textColor = variant === "primary" ? "#fff" : accentHex;
|
|
34
|
+
const iconColor = variant === "primary" ? "#fff" : accentHex;
|
|
41
35
|
|
|
42
36
|
return (
|
|
43
37
|
<View style={styles.outer}>
|
|
44
38
|
<Pressable
|
|
45
39
|
style={({ pressed }) => [
|
|
46
40
|
styles.btn,
|
|
47
|
-
variant === "
|
|
41
|
+
variant === "primary" ? styles.btnDefault : styles.btnOther,
|
|
48
42
|
variantStyle,
|
|
49
43
|
pressed && styles.pressed,
|
|
50
44
|
]}
|
|
@@ -8,10 +8,12 @@ export function SnapBadge({
|
|
|
8
8
|
}: ComponentRenderProps<Record<string, unknown>>) {
|
|
9
9
|
const { accentHex, hex } = useSnapPalette();
|
|
10
10
|
const label = String(props.label ?? "");
|
|
11
|
+
const variant = String(props.variant ?? "default");
|
|
11
12
|
const color = props.color ? String(props.color) : undefined;
|
|
12
13
|
const iconName = props.icon ? String(props.icon) : undefined;
|
|
13
14
|
const isAccent = !color || color === "accent";
|
|
14
15
|
const resolvedColor = isAccent ? accentHex : hex(color);
|
|
16
|
+
const isFilled = variant === "default";
|
|
15
17
|
|
|
16
18
|
const Icon = iconName ? ICON_MAP[iconName] : undefined;
|
|
17
19
|
|
|
@@ -19,18 +21,18 @@ export function SnapBadge({
|
|
|
19
21
|
<View
|
|
20
22
|
style={[
|
|
21
23
|
styles.badge,
|
|
22
|
-
|
|
24
|
+
isFilled
|
|
23
25
|
? { backgroundColor: resolvedColor, borderColor: resolvedColor }
|
|
24
26
|
: { borderColor: resolvedColor },
|
|
25
27
|
]}
|
|
26
28
|
>
|
|
27
29
|
{Icon && (
|
|
28
|
-
<Icon size={12} color={
|
|
30
|
+
<Icon size={12} color={isFilled ? "#fff" : resolvedColor} />
|
|
29
31
|
)}
|
|
30
32
|
<Text
|
|
31
33
|
style={[
|
|
32
34
|
styles.label,
|
|
33
|
-
{ color:
|
|
35
|
+
{ color: isFilled ? "#fff" : resolvedColor },
|
|
34
36
|
]}
|
|
35
37
|
>
|
|
36
38
|
{label}
|
|
@@ -14,12 +14,7 @@ export function SnapItem({
|
|
|
14
14
|
: undefined;
|
|
15
15
|
const variant = String(props.variant ?? "default");
|
|
16
16
|
|
|
17
|
-
const containerVariant =
|
|
18
|
-
variant === "outline"
|
|
19
|
-
? { borderWidth: 1, borderColor: colors.border + "80", borderRadius: 8, padding: 10 }
|
|
20
|
-
: variant === "muted"
|
|
21
|
-
? { backgroundColor: "rgba(255,255,255,0.04)", borderRadius: 8, padding: 10 }
|
|
22
|
-
: { paddingVertical: 8, paddingHorizontal: 10 };
|
|
17
|
+
const containerVariant = { paddingVertical: 8, paddingHorizontal: 10 };
|
|
23
18
|
|
|
24
19
|
return (
|
|
25
20
|
<View style={[styles.container, containerVariant]}>
|
|
@@ -3,14 +3,12 @@ import { StyleSheet, Text, View } from "react-native";
|
|
|
3
3
|
import { useSnapTheme } from "../theme";
|
|
4
4
|
|
|
5
5
|
const SIZE_STYLES: Record<string, { fontSize: number; lineHeight?: number; fontWeight?: "400" | "500" | "600" | "700" }> = {
|
|
6
|
-
lg: { fontSize: 20, fontWeight: "700" },
|
|
7
6
|
md: { fontSize: 16, lineHeight: 24 },
|
|
8
7
|
sm: { fontSize: 13 },
|
|
9
8
|
};
|
|
10
9
|
|
|
11
10
|
const WEIGHT_MAP: Record<string, "400" | "500" | "600" | "700"> = {
|
|
12
11
|
bold: "700",
|
|
13
|
-
medium: "500",
|
|
14
12
|
normal: "400",
|
|
15
13
|
};
|
|
16
14
|
|
package/src/ui/badge.ts
CHANGED
|
@@ -2,10 +2,12 @@ import { z } from "zod";
|
|
|
2
2
|
import { PROGRESS_COLOR_VALUES } from "../colors.js";
|
|
3
3
|
import { ICON_NAMES } from "./icon.js";
|
|
4
4
|
|
|
5
|
+
export const BADGE_VARIANTS = ["default", "outline"] as const;
|
|
5
6
|
export const BADGE_MAX_LABEL_CHARS = 30;
|
|
6
7
|
|
|
7
8
|
export const badgeProps = z.object({
|
|
8
9
|
label: z.string().min(1).max(BADGE_MAX_LABEL_CHARS),
|
|
10
|
+
variant: z.enum(BADGE_VARIANTS).optional(),
|
|
9
11
|
color: z.enum(PROGRESS_COLOR_VALUES).optional(),
|
|
10
12
|
icon: z.enum(ICON_NAMES).optional(),
|
|
11
13
|
});
|
package/src/ui/button.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { ICON_NAMES } from "./icon.js";
|
|
3
3
|
|
|
4
|
-
export const BUTTON_VARIANTS = ["
|
|
4
|
+
export const BUTTON_VARIANTS = ["secondary", "primary"] as const;
|
|
5
5
|
export const BUTTON_MAX_LABEL_CHARS = 30;
|
|
6
6
|
|
|
7
7
|
export const buttonProps = z.object({
|
package/src/ui/catalog.ts
CHANGED
|
@@ -31,7 +31,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
31
31
|
badge: {
|
|
32
32
|
props: badgeProps,
|
|
33
33
|
description:
|
|
34
|
-
"Inline label — variant: default
|
|
34
|
+
"Inline label — variant: default (filled) or outline (bordered). Optional color and icon.",
|
|
35
35
|
},
|
|
36
36
|
button: {
|
|
37
37
|
props: buttonProps,
|
|
@@ -95,7 +95,7 @@ export const snapJsonRenderCatalog = defineCatalog(snapJsonRenderSchema, {
|
|
|
95
95
|
text: {
|
|
96
96
|
props: textProps,
|
|
97
97
|
description:
|
|
98
|
-
"Text block — size:
|
|
98
|
+
"Text block — size: md (body, default), sm (caption). Optional weight and align.",
|
|
99
99
|
},
|
|
100
100
|
},
|
|
101
101
|
actions: {
|
package/src/ui/image.ts
CHANGED
package/src/ui/item.ts
CHANGED
package/src/ui/text.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
export const TEXT_SIZES = ["
|
|
4
|
-
export const TEXT_WEIGHTS = ["bold", "
|
|
3
|
+
export const TEXT_SIZES = ["md", "sm"] as const;
|
|
4
|
+
export const TEXT_WEIGHTS = ["bold", "normal"] as const;
|
|
5
5
|
export const TEXT_ALIGNS = ["left", "center", "right"] as const;
|
|
6
6
|
export const TEXT_MAX_CONTENT_CHARS = 320;
|
|
7
7
|
|