@farcaster/snap 1.14.0 → 1.15.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.
- package/dist/react/accent-context.d.ts +3 -1
- package/dist/react/accent-context.js +7 -4
- package/dist/react/components/cell-grid.js +9 -4
- package/dist/react/hooks/use-snap-colors.js +2 -3
- package/dist/react/index.js +8 -5
- package/dist/react-native/index.js +1 -1
- package/dist/ui/catalog.d.ts +1 -0
- package/dist/ui/cell-grid.d.ts +1 -0
- package/dist/ui/cell-grid.js +1 -0
- package/llms.txt +1 -0
- package/package.json +1 -1
- package/src/react/accent-context.tsx +13 -6
- package/src/react/components/cell-grid.tsx +8 -4
- package/src/react/hooks/use-snap-colors.ts +2 -3
- package/src/react/index.tsx +20 -19
- package/src/react-native/index.tsx +1 -1
- package/src/ui/cell-grid.ts +1 -0
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
export declare function SnapPreviewAccentProvider({ pageAccent, children, }: {
|
|
2
|
+
export declare function SnapPreviewAccentProvider({ pageAccent, appearance, children, }: {
|
|
3
3
|
pageAccent: string | undefined;
|
|
4
|
+
appearance?: "light" | "dark";
|
|
4
5
|
children: ReactNode;
|
|
5
6
|
}): import("react/jsx-runtime").JSX.Element;
|
|
6
7
|
export declare function useSnapPreviewPageAccent(): string | undefined;
|
|
8
|
+
export declare function useSnapAppearance(): "light" | "dark";
|
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
3
|
import { createContext, useContext } from "react";
|
|
4
|
-
const
|
|
5
|
-
export function SnapPreviewAccentProvider({ pageAccent, children, }) {
|
|
6
|
-
return (_jsx(
|
|
4
|
+
const SnapPreviewContext = createContext(null);
|
|
5
|
+
export function SnapPreviewAccentProvider({ pageAccent, appearance = "dark", children, }) {
|
|
6
|
+
return (_jsx(SnapPreviewContext.Provider, { value: { pageAccent, appearance }, children: children }));
|
|
7
7
|
}
|
|
8
8
|
export function useSnapPreviewPageAccent() {
|
|
9
|
-
return useContext(
|
|
9
|
+
return useContext(SnapPreviewContext)?.pageAccent;
|
|
10
|
+
}
|
|
11
|
+
export function useSnapAppearance() {
|
|
12
|
+
return useContext(SnapPreviewContext)?.appearance ?? "dark";
|
|
10
13
|
}
|
|
@@ -16,6 +16,7 @@ export function SnapCellGrid({ element: { props }, }) {
|
|
|
16
16
|
const gap = String(props.gap ?? "sm");
|
|
17
17
|
const gapMap = { none: 0, sm: 1, md: 2, lg: 4 };
|
|
18
18
|
const gapPx = gapMap[gap] ?? 1;
|
|
19
|
+
const rowHeight = typeof props.rowHeight === "number" ? props.rowHeight : 28;
|
|
19
20
|
const name = props.name ? String(props.name) : POST_GRID_TAP_KEY;
|
|
20
21
|
const tapPath = `/inputs/${name}`;
|
|
21
22
|
const tapRaw = get(tapPath);
|
|
@@ -62,9 +63,9 @@ export function SnapCellGrid({ element: { props }, }) {
|
|
|
62
63
|
handleTap(r, c);
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
|
-
: undefined, className: cn("flex
|
|
66
|
+
: undefined, className: cn("flex items-center justify-center rounded text-xs font-semibold", interactive ? "cursor-pointer select-none" : "cursor-default"), style: {
|
|
67
|
+
height: rowHeight,
|
|
66
68
|
background: bg,
|
|
67
|
-
// Two-layer ring: 1px white/black inner + 2px accent outer
|
|
68
69
|
boxShadow: selected
|
|
69
70
|
? `inset 0 0 0 1px ${colors.mode === "dark" ? "#000" : "#fff"}, inset 0 0 0 2px ${colors.mode === "dark" ? "#fff" : "#000"}`
|
|
70
71
|
: undefined,
|
|
@@ -74,9 +75,13 @@ export function SnapCellGrid({ element: { props }, }) {
|
|
|
74
75
|
const selectionLabel = interactive && selectedSet.size > 0
|
|
75
76
|
? `inputs.${name}: ${[...selectedSet].join(isMultiple ? " | " : "")}`
|
|
76
77
|
: null;
|
|
77
|
-
return (_jsxs("div", { children: [_jsx("div", {
|
|
78
|
-
|
|
78
|
+
return (_jsxs("div", { children: [_jsx("div", { style: {
|
|
79
|
+
display: "grid",
|
|
80
|
+
width: "100%",
|
|
81
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
79
82
|
gap: gapPx,
|
|
83
|
+
padding: 4,
|
|
84
|
+
borderRadius: 8,
|
|
80
85
|
backgroundColor: colors.muted,
|
|
81
86
|
}, children: cellEls }), selectionLabel && (_jsx("div", { className: "mt-1.5 truncate text-xs font-mono", style: { color: colors.textMuted }, children: selectionLabel }))] }));
|
|
82
87
|
}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useMemo } from "react";
|
|
3
3
|
import { useStateStore } from "@json-render/react";
|
|
4
|
-
import { useColorMode } from "@neynar/ui/color-mode";
|
|
5
4
|
import { resolveSnapPaletteHex } from "../lib/resolve-palette-hex.js";
|
|
6
|
-
import { useSnapPreviewPageAccent } from "../accent-context.js";
|
|
5
|
+
import { useSnapPreviewPageAccent, useSnapAppearance } from "../accent-context.js";
|
|
7
6
|
import { PALETTE_DARK_HEX, PALETTE_LIGHT_HEX } from "@farcaster/snap";
|
|
8
7
|
/** Readable foreground color (black or white) for a given hex background. */
|
|
9
8
|
export function pickForegroundForBg(hex) {
|
|
@@ -69,7 +68,7 @@ function buildSnapColors(accentName, mode) {
|
|
|
69
68
|
*/
|
|
70
69
|
export function useSnapColors() {
|
|
71
70
|
const { get } = useStateStore();
|
|
72
|
-
const
|
|
71
|
+
const mode = useSnapAppearance();
|
|
73
72
|
const pageAccent = useSnapPreviewPageAccent();
|
|
74
73
|
const fromState = get("/theme/accent");
|
|
75
74
|
const accentRaw = (typeof pageAccent === "string" && pageAccent.length > 0
|
package/dist/react/index.js
CHANGED
|
@@ -174,7 +174,7 @@ export function SnapView({ snap, handlers, loading = false, appearance = "dark",
|
|
|
174
174
|
break;
|
|
175
175
|
}
|
|
176
176
|
}, [handlers]);
|
|
177
|
-
return (_jsxs("div", { style: { position: "relative", width: "100%" }, children: [showConfetti && _jsx(ConfettiOverlay, {}),
|
|
177
|
+
return (_jsxs("div", { style: { position: "relative", width: "100%" }, children: [showConfetti && _jsx(ConfettiOverlay, {}), _jsx("div", { style: {
|
|
178
178
|
position: "absolute",
|
|
179
179
|
inset: 0,
|
|
180
180
|
display: "flex",
|
|
@@ -182,10 +182,13 @@ export function SnapView({ snap, handlers, loading = false, appearance = "dark",
|
|
|
182
182
|
justifyContent: "center",
|
|
183
183
|
zIndex: 10,
|
|
184
184
|
fontSize: 14,
|
|
185
|
-
color: "
|
|
186
|
-
background: "
|
|
187
|
-
backdropFilter: "blur(
|
|
188
|
-
|
|
185
|
+
color: appearance === "dark" ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.4)",
|
|
186
|
+
background: appearance === "dark" ? "rgba(0,0,0,0.3)" : "rgba(255,255,255,0.5)",
|
|
187
|
+
backdropFilter: loading ? "blur(8px)" : "blur(0px)",
|
|
188
|
+
opacity: loading ? 1 : 0,
|
|
189
|
+
pointerEvents: loading ? "auto" : "none",
|
|
190
|
+
transition: "opacity 0.3s ease, backdrop-filter 0.3s ease",
|
|
191
|
+
}, children: "Loading..." }), _jsx("div", { style: previewSurfaceStyle, children: _jsx(SnapPreviewAccentProvider, { pageAccent: snap.theme?.accent, appearance: appearance, children: _jsx(SnapCatalogView, { spec: spec, state: initialState, loading: false, onStateChange: (changes) => {
|
|
189
192
|
applyStatePaths(stateRef.current, changes);
|
|
190
193
|
}, onAction: handleAction }, pageKey) }) })] }));
|
|
191
194
|
}
|
|
@@ -133,7 +133,7 @@ function SnapViewInner({ snap, handlers, loading = false, }) {
|
|
|
133
133
|
return (_jsxs(View, { style: styles.container, children: [loading && (_jsx(View, { style: [
|
|
134
134
|
styles.overlay,
|
|
135
135
|
{
|
|
136
|
-
backgroundColor: mode === "dark" ? "rgba(0,0,0,0.
|
|
136
|
+
backgroundColor: mode === "dark" ? "rgba(0,0,0,0.3)" : "rgba(255,255,255,0.5)",
|
|
137
137
|
},
|
|
138
138
|
], children: _jsx(ActivityIndicator, { size: "large", color: accentHex }) })), _jsx(SnapCatalogView, { spec: spec, state: initialState, loading: false, onStateChange: (changes) => {
|
|
139
139
|
applyStatePaths(stateRef.current, changes);
|
package/dist/ui/catalog.d.ts
CHANGED
package/dist/ui/cell-grid.d.ts
CHANGED
package/dist/ui/cell-grid.js
CHANGED
|
@@ -14,6 +14,7 @@ export const cellGridProps = z
|
|
|
14
14
|
rows: z.number().int().min(GRID_MIN_ROWS).max(GRID_MAX_ROWS),
|
|
15
15
|
cells: z.array(cellGridCellSchema),
|
|
16
16
|
gap: z.enum(GRID_GAP_VALUES).optional(),
|
|
17
|
+
rowHeight: z.number().int().min(8).max(64).optional(),
|
|
17
18
|
select: z.enum(["off", "single", "multiple"]).optional(),
|
|
18
19
|
})
|
|
19
20
|
.superRefine((val, ctx) => {
|
package/llms.txt
CHANGED
|
@@ -88,6 +88,7 @@ Top-level fields: `version` (required, `"1.0"`), `theme` (optional, `{ accent: P
|
|
|
88
88
|
- `rows` (number, required, 2–16)
|
|
89
89
|
- `cells` (array, required): sparse list of `{ row, col, color?: PaletteColor, content?: string }`
|
|
90
90
|
- `gap` (optional): `"none"` (0px) | `"sm"` (1px) | `"md"` (2px) | `"lg"` (4px). Default: `"sm"`
|
|
91
|
+
- `rowHeight` (number, optional, 8–64): pixel height per row. Default: 28. Grid height = rows × rowHeight
|
|
91
92
|
- `select` (optional): `"off"` | `"single"` | `"multiple"`. Default: `"off"`. Taps write to `inputs[name]`
|
|
92
93
|
|
|
93
94
|
### Container Components
|
package/package.json
CHANGED
|
@@ -2,28 +2,35 @@
|
|
|
2
2
|
|
|
3
3
|
import { createContext, useContext, type ReactNode } from "react";
|
|
4
4
|
|
|
5
|
-
type
|
|
5
|
+
type SnapPreviewContextValue = {
|
|
6
6
|
/** From loaded snap `page.theme.accent` (undefined if the snap omits it). */
|
|
7
7
|
pageAccent: string | undefined;
|
|
8
|
+
/** Light/dark appearance passed from SnapView. */
|
|
9
|
+
appearance: "light" | "dark";
|
|
8
10
|
};
|
|
9
11
|
|
|
10
|
-
const
|
|
11
|
-
createContext<SnapPreviewAccentContextValue | null>(null);
|
|
12
|
+
const SnapPreviewContext = createContext<SnapPreviewContextValue | null>(null);
|
|
12
13
|
|
|
13
14
|
export function SnapPreviewAccentProvider({
|
|
14
15
|
pageAccent,
|
|
16
|
+
appearance = "dark",
|
|
15
17
|
children,
|
|
16
18
|
}: {
|
|
17
19
|
pageAccent: string | undefined;
|
|
20
|
+
appearance?: "light" | "dark";
|
|
18
21
|
children: ReactNode;
|
|
19
22
|
}) {
|
|
20
23
|
return (
|
|
21
|
-
<
|
|
24
|
+
<SnapPreviewContext.Provider value={{ pageAccent, appearance }}>
|
|
22
25
|
{children}
|
|
23
|
-
</
|
|
26
|
+
</SnapPreviewContext.Provider>
|
|
24
27
|
);
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
export function useSnapPreviewPageAccent(): string | undefined {
|
|
28
|
-
return useContext(
|
|
31
|
+
return useContext(SnapPreviewContext)?.pageAccent;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function useSnapAppearance(): "light" | "dark" {
|
|
35
|
+
return useContext(SnapPreviewContext)?.appearance ?? "dark";
|
|
29
36
|
}
|
|
@@ -22,6 +22,7 @@ export function SnapCellGrid({
|
|
|
22
22
|
const gap = String(props.gap ?? "sm");
|
|
23
23
|
const gapMap: Record<string, number> = { none: 0, sm: 1, md: 2, lg: 4 };
|
|
24
24
|
const gapPx = gapMap[gap] ?? 1;
|
|
25
|
+
const rowHeight = typeof props.rowHeight === "number" ? props.rowHeight : 28;
|
|
25
26
|
|
|
26
27
|
const name = props.name ? String(props.name) : POST_GRID_TAP_KEY;
|
|
27
28
|
const tapPath = `/inputs/${name}`;
|
|
@@ -81,12 +82,12 @@ export function SnapCellGrid({
|
|
|
81
82
|
: undefined
|
|
82
83
|
}
|
|
83
84
|
className={cn(
|
|
84
|
-
"flex
|
|
85
|
+
"flex items-center justify-center rounded text-xs font-semibold",
|
|
85
86
|
interactive ? "cursor-pointer select-none" : "cursor-default",
|
|
86
87
|
)}
|
|
87
88
|
style={{
|
|
89
|
+
height: rowHeight,
|
|
88
90
|
background: bg,
|
|
89
|
-
// Two-layer ring: 1px white/black inner + 2px accent outer
|
|
90
91
|
boxShadow: selected
|
|
91
92
|
? `inset 0 0 0 1px ${colors.mode === "dark" ? "#000" : "#fff"}, inset 0 0 0 2px ${colors.mode === "dark" ? "#fff" : "#000"}`
|
|
92
93
|
: undefined,
|
|
@@ -105,10 +106,13 @@ export function SnapCellGrid({
|
|
|
105
106
|
return (
|
|
106
107
|
<div>
|
|
107
108
|
<div
|
|
108
|
-
className="grid w-full rounded-lg p-1"
|
|
109
109
|
style={{
|
|
110
|
-
|
|
110
|
+
display: "grid",
|
|
111
|
+
width: "100%",
|
|
112
|
+
gridTemplateColumns: `repeat(${cols}, 1fr)`,
|
|
111
113
|
gap: gapPx,
|
|
114
|
+
padding: 4,
|
|
115
|
+
borderRadius: 8,
|
|
112
116
|
backgroundColor: colors.muted,
|
|
113
117
|
}}
|
|
114
118
|
>
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import { useMemo } from "react";
|
|
4
4
|
import { useStateStore } from "@json-render/react";
|
|
5
|
-
import { useColorMode } from "@neynar/ui/color-mode";
|
|
6
5
|
import { resolveSnapPaletteHex } from "../lib/resolve-palette-hex";
|
|
7
|
-
import { useSnapPreviewPageAccent } from "../accent-context";
|
|
6
|
+
import { useSnapPreviewPageAccent, useSnapAppearance } from "../accent-context";
|
|
8
7
|
import type { PaletteColor } from "@farcaster/snap";
|
|
9
8
|
import { PALETTE_DARK_HEX, PALETTE_LIGHT_HEX } from "@farcaster/snap";
|
|
10
9
|
|
|
@@ -113,7 +112,7 @@ function buildSnapColors(
|
|
|
113
112
|
*/
|
|
114
113
|
export function useSnapColors(): SnapColors {
|
|
115
114
|
const { get } = useStateStore();
|
|
116
|
-
const
|
|
115
|
+
const mode = useSnapAppearance();
|
|
117
116
|
const pageAccent = useSnapPreviewPageAccent();
|
|
118
117
|
const fromState = get("/theme/accent");
|
|
119
118
|
const accentRaw =
|
package/src/react/index.tsx
CHANGED
|
@@ -283,27 +283,28 @@ export function SnapView({
|
|
|
283
283
|
return (
|
|
284
284
|
<div style={{ position: "relative", width: "100%" }}>
|
|
285
285
|
{showConfetti && <ConfettiOverlay />}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
286
|
+
<div
|
|
287
|
+
style={{
|
|
288
|
+
position: "absolute",
|
|
289
|
+
inset: 0,
|
|
290
|
+
display: "flex",
|
|
291
|
+
alignItems: "center",
|
|
292
|
+
justifyContent: "center",
|
|
293
|
+
zIndex: 10,
|
|
294
|
+
fontSize: 14,
|
|
295
|
+
color: appearance === "dark" ? "rgba(255,255,255,0.5)" : "rgba(0,0,0,0.4)",
|
|
296
|
+
background: appearance === "dark" ? "rgba(0,0,0,0.3)" : "rgba(255,255,255,0.5)",
|
|
297
|
+
backdropFilter: loading ? "blur(8px)" : "blur(0px)",
|
|
298
|
+
opacity: loading ? 1 : 0,
|
|
299
|
+
pointerEvents: loading ? "auto" : "none",
|
|
300
|
+
transition: "opacity 0.3s ease, backdrop-filter 0.3s ease",
|
|
301
|
+
}}
|
|
302
|
+
>
|
|
303
|
+
Loading...
|
|
304
|
+
</div>
|
|
304
305
|
|
|
305
306
|
<div style={previewSurfaceStyle}>
|
|
306
|
-
<SnapPreviewAccentProvider pageAccent={snap.theme?.accent}>
|
|
307
|
+
<SnapPreviewAccentProvider pageAccent={snap.theme?.accent} appearance={appearance}>
|
|
307
308
|
<SnapCatalogView
|
|
308
309
|
key={pageKey}
|
|
309
310
|
spec={spec}
|
package/src/ui/cell-grid.ts
CHANGED
|
@@ -22,6 +22,7 @@ export const cellGridProps = z
|
|
|
22
22
|
rows: z.number().int().min(GRID_MIN_ROWS).max(GRID_MAX_ROWS),
|
|
23
23
|
cells: z.array(cellGridCellSchema),
|
|
24
24
|
gap: z.enum(GRID_GAP_VALUES).optional(),
|
|
25
|
+
rowHeight: z.number().int().min(8).max(64).optional(),
|
|
25
26
|
select: z.enum(["off", "single", "multiple"]).optional(),
|
|
26
27
|
})
|
|
27
28
|
.superRefine((val, ctx) => {
|