@farcaster/snap 2.0.0 → 2.0.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.
Files changed (194) hide show
  1. package/dist/colors.d.ts +4 -4
  2. package/dist/colors.js +20 -20
  3. package/dist/constants.d.ts +17 -1
  4. package/dist/constants.js +19 -1
  5. package/dist/index.d.ts +4 -6
  6. package/dist/index.js +2 -4
  7. package/dist/react/accent-context.d.ts +3 -1
  8. package/dist/react/accent-context.js +7 -4
  9. package/dist/react/catalog-renderer.js +4 -0
  10. package/dist/react/components/action-button.d.ts +2 -1
  11. package/dist/react/components/action-button.js +32 -13
  12. package/dist/react/components/badge.js +8 -8
  13. package/dist/react/components/bar-chart.d.ts +5 -0
  14. package/dist/react/components/bar-chart.js +26 -0
  15. package/dist/react/components/cell-grid.d.ts +5 -0
  16. package/dist/react/components/cell-grid.js +87 -0
  17. package/dist/react/components/icon.js +4 -10
  18. package/dist/react/components/input.js +12 -6
  19. package/dist/react/components/item-group.js +3 -1
  20. package/dist/react/components/item.d.ts +3 -3
  21. package/dist/react/components/item.js +4 -3
  22. package/dist/react/components/progress.js +3 -3
  23. package/dist/react/components/separator.js +3 -1
  24. package/dist/react/components/slider.js +15 -10
  25. package/dist/react/components/switch.js +10 -12
  26. package/dist/react/components/text.js +6 -14
  27. package/dist/react/components/toggle-group.js +20 -6
  28. package/dist/react/hooks/use-snap-colors.d.ts +38 -0
  29. package/dist/react/hooks/use-snap-colors.js +81 -0
  30. package/dist/react/index.d.ts +13 -1
  31. package/dist/react/index.js +9 -188
  32. package/dist/react/snap-view-core.d.ts +11 -0
  33. package/dist/react/snap-view-core.js +224 -0
  34. package/dist/react/v1/snap-view.d.ts +16 -0
  35. package/dist/react/v1/snap-view.js +90 -0
  36. package/dist/react/v2/snap-view.d.ts +23 -0
  37. package/dist/react/v2/snap-view.js +91 -0
  38. package/dist/react-native/catalog-renderer.d.ts +5 -0
  39. package/dist/react-native/catalog-renderer.js +40 -0
  40. package/dist/react-native/components/snap-action-button.d.ts +2 -0
  41. package/dist/react-native/components/snap-action-button.js +69 -0
  42. package/dist/react-native/components/snap-badge.d.ts +2 -0
  43. package/dist/react-native/components/snap-badge.js +41 -0
  44. package/dist/react-native/components/snap-bar-chart.d.ts +2 -0
  45. package/dist/react-native/components/snap-bar-chart.js +39 -0
  46. package/dist/react-native/components/snap-cell-grid.d.ts +2 -0
  47. package/dist/react-native/components/snap-cell-grid.js +94 -0
  48. package/dist/react-native/components/snap-icon.d.ts +5 -0
  49. package/dist/react-native/components/snap-icon.js +56 -0
  50. package/dist/react-native/components/snap-image.d.ts +2 -0
  51. package/dist/react-native/components/snap-image.js +23 -0
  52. package/dist/react-native/components/snap-input.d.ts +2 -0
  53. package/dist/react-native/components/snap-input.js +37 -0
  54. package/dist/react-native/components/snap-item-group.d.ts +5 -0
  55. package/dist/react-native/components/snap-item-group.js +23 -0
  56. package/dist/react-native/components/snap-item.d.ts +5 -0
  57. package/dist/react-native/components/snap-item.js +42 -0
  58. package/dist/react-native/components/snap-progress.d.ts +2 -0
  59. package/dist/react-native/components/snap-progress.js +26 -0
  60. package/dist/react-native/components/snap-separator.d.ts +2 -0
  61. package/dist/react-native/components/snap-separator.js +23 -0
  62. package/dist/react-native/components/snap-slider.d.ts +2 -0
  63. package/dist/react-native/components/snap-slider.js +43 -0
  64. package/dist/react-native/components/snap-stack.d.ts +5 -0
  65. package/dist/react-native/components/snap-stack.js +49 -0
  66. package/dist/react-native/components/snap-switch.d.ts +2 -0
  67. package/dist/react-native/components/snap-switch.js +31 -0
  68. package/dist/react-native/components/snap-text.d.ts +2 -0
  69. package/dist/react-native/components/snap-text.js +35 -0
  70. package/dist/react-native/components/snap-toggle-group.d.ts +2 -0
  71. package/dist/react-native/components/snap-toggle-group.js +99 -0
  72. package/dist/react-native/confetti-overlay.d.ts +1 -0
  73. package/dist/react-native/confetti-overlay.js +106 -0
  74. package/dist/react-native/index.d.ts +28 -0
  75. package/dist/react-native/index.js +15 -0
  76. package/dist/react-native/snap-view-core.d.ts +11 -0
  77. package/dist/react-native/snap-view-core.js +153 -0
  78. package/dist/react-native/theme.d.ts +27 -0
  79. package/dist/react-native/theme.js +43 -0
  80. package/dist/react-native/types.d.ts +42 -0
  81. package/dist/react-native/types.js +1 -0
  82. package/dist/react-native/use-snap-palette.d.ts +13 -0
  83. package/dist/react-native/use-snap-palette.js +48 -0
  84. package/dist/react-native/v1/snap-view.d.ts +24 -0
  85. package/dist/react-native/v1/snap-view.js +96 -0
  86. package/dist/react-native/v2/snap-view.d.ts +33 -0
  87. package/dist/react-native/v2/snap-view.js +114 -0
  88. package/dist/schemas.d.ts +100 -13
  89. package/dist/schemas.js +28 -10
  90. package/dist/server/parseRequest.d.ts +10 -0
  91. package/dist/server/parseRequest.js +48 -7
  92. package/dist/server/verify.d.ts +1 -0
  93. package/dist/server/verify.js +1 -0
  94. package/dist/ui/badge.d.ts +7 -2
  95. package/dist/ui/badge.js +2 -0
  96. package/dist/ui/bar-chart.d.ts +30 -0
  97. package/dist/ui/bar-chart.js +30 -0
  98. package/dist/ui/button.d.ts +4 -6
  99. package/dist/ui/button.js +1 -1
  100. package/dist/ui/catalog.d.ts +90 -16
  101. package/dist/ui/catalog.js +17 -3
  102. package/dist/ui/cell-grid.d.ts +34 -0
  103. package/dist/ui/cell-grid.js +39 -0
  104. package/dist/ui/icon.d.ts +2 -2
  105. package/dist/ui/image.d.ts +1 -2
  106. package/dist/ui/image.js +1 -1
  107. package/dist/ui/index.d.ts +4 -0
  108. package/dist/ui/index.js +2 -0
  109. package/dist/ui/item.d.ts +1 -3
  110. package/dist/ui/item.js +1 -1
  111. package/dist/ui/schema.d.ts +6 -2
  112. package/dist/ui/schema.js +2 -2
  113. package/dist/ui/slider.d.ts +1 -0
  114. package/dist/ui/slider.js +2 -0
  115. package/dist/ui/text.d.ts +2 -4
  116. package/dist/ui/text.js +2 -2
  117. package/dist/validator.d.ts +3 -2
  118. package/dist/validator.js +198 -2
  119. package/llms.txt +199 -0
  120. package/package.json +9 -3
  121. package/src/colors.ts +20 -20
  122. package/src/constants.ts +23 -1
  123. package/src/index.ts +16 -13
  124. package/src/react/accent-context.tsx +13 -6
  125. package/src/react/catalog-renderer.tsx +4 -0
  126. package/src/react/components/action-button.tsx +47 -20
  127. package/src/react/components/badge.tsx +14 -18
  128. package/src/react/components/bar-chart.tsx +69 -0
  129. package/src/react/components/cell-grid.tsx +128 -0
  130. package/src/react/components/icon.tsx +5 -18
  131. package/src/react/components/input.tsx +20 -9
  132. package/src/react/components/item-group.tsx +4 -1
  133. package/src/react/components/item.tsx +13 -10
  134. package/src/react/components/progress.tsx +12 -7
  135. package/src/react/components/separator.tsx +8 -1
  136. package/src/react/components/slider.tsx +28 -15
  137. package/src/react/components/switch.tsx +12 -16
  138. package/src/react/components/text.tsx +14 -23
  139. package/src/react/components/toggle-group.tsx +26 -9
  140. package/src/react/hooks/use-snap-colors.ts +128 -0
  141. package/src/react/index.tsx +49 -265
  142. package/src/react/snap-view-core.tsx +340 -0
  143. package/src/react/v1/snap-view.tsx +176 -0
  144. package/src/react/v2/snap-view.tsx +199 -0
  145. package/src/react-native/catalog-renderer.tsx +41 -0
  146. package/src/react-native/components/snap-action-button.tsx +96 -0
  147. package/src/react-native/components/snap-badge.tsx +60 -0
  148. package/src/react-native/components/snap-bar-chart.tsx +73 -0
  149. package/src/react-native/components/snap-cell-grid.tsx +150 -0
  150. package/src/react-native/components/snap-icon.tsx +102 -0
  151. package/src/react-native/components/snap-image.tsx +37 -0
  152. package/src/react-native/components/snap-input.tsx +58 -0
  153. package/src/react-native/components/snap-item-group.tsx +43 -0
  154. package/src/react-native/components/snap-item.tsx +66 -0
  155. package/src/react-native/components/snap-progress.tsx +40 -0
  156. package/src/react-native/components/snap-separator.tsx +32 -0
  157. package/src/react-native/components/snap-slider.tsx +85 -0
  158. package/src/react-native/components/snap-stack.tsx +66 -0
  159. package/src/react-native/components/snap-switch.tsx +46 -0
  160. package/src/react-native/components/snap-text.tsx +51 -0
  161. package/src/react-native/components/snap-toggle-group.tsx +127 -0
  162. package/src/react-native/confetti-overlay.tsx +134 -0
  163. package/src/react-native/index.tsx +83 -0
  164. package/src/react-native/snap-view-core.tsx +209 -0
  165. package/src/react-native/theme.tsx +85 -0
  166. package/src/react-native/types.ts +38 -0
  167. package/src/react-native/use-snap-palette.ts +64 -0
  168. package/src/react-native/v1/snap-view.tsx +229 -0
  169. package/src/react-native/v2/snap-view.tsx +283 -0
  170. package/src/schemas.ts +68 -17
  171. package/src/server/parseRequest.ts +68 -9
  172. package/src/server/verify.ts +2 -0
  173. package/src/ui/README.md +8 -8
  174. package/src/ui/badge.ts +2 -0
  175. package/src/ui/bar-chart.ts +38 -0
  176. package/src/ui/button.ts +1 -1
  177. package/src/ui/catalog.ts +19 -3
  178. package/src/ui/cell-grid.ts +49 -0
  179. package/src/ui/image.ts +1 -1
  180. package/src/ui/index.ts +6 -0
  181. package/src/ui/item.ts +1 -1
  182. package/src/ui/schema.ts +2 -2
  183. package/src/ui/slider.ts +2 -0
  184. package/src/ui/text.ts +2 -2
  185. package/src/validator.ts +246 -2
  186. package/dist/dataStore.d.ts +0 -12
  187. package/dist/dataStore.js +0 -35
  188. package/dist/middleware.d.ts +0 -3
  189. package/dist/middleware.js +0 -3
  190. package/dist/react/hooks/use-snap-accent.d.ts +0 -13
  191. package/dist/react/hooks/use-snap-accent.js +0 -32
  192. package/src/dataStore.ts +0 -62
  193. package/src/middleware.ts +0 -7
  194. package/src/react/hooks/use-snap-accent.ts +0 -45
package/dist/colors.d.ts CHANGED
@@ -3,12 +3,12 @@
3
3
  * it to a hex value appropriate for its current light/dark mode.
4
4
  *
5
5
  * Light-mode hex values (used by emulator):
6
- * gray=#8F8F8F blue=#006BFF red=#FC0036 amber=#FFAE00
7
- * green=#28A948 teal=#00AC96 purple=#8B5CF6 pink=#F32782
6
+ * gray=#6E6A86 blue=#286983 red=#B4637A amber=#EA9D34
7
+ * green=#3E8F8F teal=#56949F purple=#907AA9 pink=#D7827E
8
8
  *
9
9
  * Dark-mode hex values (for reference; client-owned):
10
- * gray=#8F8F8F blue=#006FFE red=#F13342 amber=#FFAE00
11
- * green=#00AC3A teal=#00AA96 purple=#A78BFA pink=#F12B82
10
+ * gray=#908CAA blue=#9CCFD8 red=#EB6F92 amber=#F6C177
11
+ * green=#56D4A4 teal=#3E8FB0 purple=#C4A7E7 pink=#EBBCBA
12
12
  */
13
13
  export declare const PALETTE_COLOR: {
14
14
  readonly gray: "gray";
package/dist/colors.js CHANGED
@@ -3,12 +3,12 @@
3
3
  * it to a hex value appropriate for its current light/dark mode.
4
4
  *
5
5
  * Light-mode hex values (used by emulator):
6
- * gray=#8F8F8F blue=#006BFF red=#FC0036 amber=#FFAE00
7
- * green=#28A948 teal=#00AC96 purple=#8B5CF6 pink=#F32782
6
+ * gray=#6E6A86 blue=#286983 red=#B4637A amber=#EA9D34
7
+ * green=#3E8F8F teal=#56949F purple=#907AA9 pink=#D7827E
8
8
  *
9
9
  * Dark-mode hex values (for reference; client-owned):
10
- * gray=#8F8F8F blue=#006FFE red=#F13342 amber=#FFAE00
11
- * green=#00AC3A teal=#00AA96 purple=#A78BFA pink=#F12B82
10
+ * gray=#908CAA blue=#9CCFD8 red=#EB6F92 amber=#F6C177
11
+ * green=#56D4A4 teal=#3E8FB0 purple=#C4A7E7 pink=#EBBCBA
12
12
  */
13
13
  export const PALETTE_COLOR = {
14
14
  gray: "gray",
@@ -34,25 +34,25 @@ export const PALETTE_COLOR_VALUES = [
34
34
  ];
35
35
  /** Light-mode hex for each palette color (emulator / reference client). */
36
36
  export const PALETTE_LIGHT_HEX = {
37
- gray: "#8F8F8F",
38
- blue: "#006BFF",
39
- red: "#FC0036",
40
- amber: "#FFAE00",
41
- green: "#28A948",
42
- teal: "#00AC96",
43
- purple: "#8B5CF6",
44
- pink: "#F32782",
37
+ gray: "#6E6A86",
38
+ blue: "#286983",
39
+ red: "#B4637A",
40
+ amber: "#EA9D34",
41
+ green: "#3E8F8F",
42
+ teal: "#56949F",
43
+ purple: "#907AA9",
44
+ pink: "#D7827E",
45
45
  };
46
46
  /** Dark-mode hex for each palette color (reference). */
47
47
  export const PALETTE_DARK_HEX = {
48
- gray: "#8F8F8F",
49
- blue: "#006FFE",
50
- red: "#F13342",
51
- amber: "#FFAE00",
52
- green: "#00AC3A",
53
- teal: "#00AA96",
54
- purple: "#A78BFA",
55
- pink: "#F12B82",
48
+ gray: "#908CAA",
49
+ blue: "#9CCFD8",
50
+ red: "#EB6F92",
51
+ amber: "#F6C177",
52
+ green: "#56D4A4",
53
+ teal: "#3E8FB0",
54
+ purple: "#C4A7E7",
55
+ pink: "#EBBCBA",
56
56
  };
57
57
  export const PROGRESS_COLOR_VALUES = [
58
58
  PALETTE_COLOR_ACCENT,
@@ -1,3 +1,19 @@
1
- export declare const SPEC_VERSION: "1.0";
1
+ export declare const SPEC_VERSION_1: "1.0";
2
+ export declare const SPEC_VERSION_2: "2.0";
3
+ export declare const SPEC_VERSION: "2.0";
4
+ export declare const SUPPORTED_SPEC_VERSIONS: readonly ["1.0", "2.0"];
5
+ export type SpecVersion = (typeof SUPPORTED_SPEC_VERSIONS)[number];
2
6
  export declare const MEDIA_TYPE: "application/vnd.farcaster.snap+json";
3
7
  export declare const EFFECT_VALUES: readonly ["confetti"];
8
+ export declare const POST_GRID_TAP_KEY: "grid_tap";
9
+ export declare const GRID_MIN_COLS = 2;
10
+ export declare const GRID_MAX_COLS = 32;
11
+ export declare const GRID_MIN_ROWS = 2;
12
+ export declare const GRID_MAX_ROWS = 16;
13
+ export declare const GRID_GAP_VALUES: readonly ["none", "sm", "md", "lg"];
14
+ export declare const MAX_ELEMENTS = 64;
15
+ export declare const MAX_ROOT_CHILDREN = 7;
16
+ export declare const MAX_CHILDREN = 6;
17
+ export declare const MAX_DEPTH = 4;
18
+ export declare const BAR_CHART_MAX_BARS = 6;
19
+ export declare const BAR_CHART_LABEL_MAX_CHARS = 40;
package/dist/constants.js CHANGED
@@ -1,3 +1,21 @@
1
- export const SPEC_VERSION = "1.0";
1
+ export const SPEC_VERSION_1 = "1.0";
2
+ export const SPEC_VERSION_2 = "2.0";
3
+ export const SPEC_VERSION = SPEC_VERSION_2;
4
+ export const SUPPORTED_SPEC_VERSIONS = [SPEC_VERSION_1, SPEC_VERSION_2];
2
5
  export const MEDIA_TYPE = "application/vnd.farcaster.snap+json";
3
6
  export const EFFECT_VALUES = ["confetti"];
7
+ // ─── Pixel grid ────────────────────────────────────────
8
+ export const POST_GRID_TAP_KEY = "grid_tap";
9
+ export const GRID_MIN_COLS = 2;
10
+ export const GRID_MAX_COLS = 32;
11
+ export const GRID_MIN_ROWS = 2;
12
+ export const GRID_MAX_ROWS = 16;
13
+ export const GRID_GAP_VALUES = ["none", "sm", "md", "lg"];
14
+ // ─── Snap structural limits ───────────────────────────
15
+ export const MAX_ELEMENTS = 64;
16
+ export const MAX_ROOT_CHILDREN = 7;
17
+ export const MAX_CHILDREN = 6;
18
+ export const MAX_DEPTH = 4;
19
+ // ─── Bar chart ─────────────────────────────────────────
20
+ export const BAR_CHART_MAX_BARS = 6;
21
+ export const BAR_CHART_LABEL_MAX_CHARS = 40;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,5 @@
1
- export type { Spec as SnapSpec, UIElement as SnapUIElement } from "@json-render/core";
2
- export { SPEC_VERSION, MEDIA_TYPE, EFFECT_VALUES, } from "./constants.js";
1
+ export type { Spec as SnapSpec, UIElement as SnapUIElement, } from "@json-render/core";
2
+ export { SPEC_VERSION, SPEC_VERSION_1, SPEC_VERSION_2, SUPPORTED_SPEC_VERSIONS, type SpecVersion, MEDIA_TYPE, EFFECT_VALUES, POST_GRID_TAP_KEY, MAX_ELEMENTS, MAX_ROOT_CHILDREN, MAX_CHILDREN, MAX_DEPTH, } from "./constants.js";
3
3
  export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, type PaletteColor, } from "./colors.js";
4
- export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, payloadSchema, type SnapAction, type SnapContext, type SnapResponse, type SnapHandlerResult, type SnapFunction, type SnapPayload, } from "./schemas.js";
5
- export { validateSnapResponse, type ValidationResult, } from "./validator.js";
6
- export { type DataStoreValue, type SnapDataStore, type SnapDataStoreOperations, createDefaultDataStore, createInMemoryDataStore, } from "./dataStore.js";
7
- export { type Middleware, useMiddleware } from "./middleware.js";
4
+ export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, payloadSchema, type SnapAction, type SnapContext, type SnapResponse, type SnapHandlerResult, type SnapElementInput, type SnapSpecInput, type SnapFunction, type SnapPayload, } from "./schemas.js";
5
+ export { validateSnapResponse, type ValidationResult } from "./validator.js";
package/dist/index.js CHANGED
@@ -1,6 +1,4 @@
1
- export { SPEC_VERSION, MEDIA_TYPE, EFFECT_VALUES, } from "./constants.js";
1
+ export { SPEC_VERSION, SPEC_VERSION_1, SPEC_VERSION_2, SUPPORTED_SPEC_VERSIONS, MEDIA_TYPE, EFFECT_VALUES, POST_GRID_TAP_KEY, MAX_ELEMENTS, MAX_ROOT_CHILDREN, MAX_CHILDREN, MAX_DEPTH, } from "./constants.js";
2
2
  export { DEFAULT_THEME_ACCENT, PALETTE_COLOR, PALETTE_COLOR_ACCENT, PALETTE_COLOR_VALUES, PALETTE_LIGHT_HEX, PALETTE_DARK_HEX, } from "./colors.js";
3
3
  export { ACTION_TYPE_GET, ACTION_TYPE_POST, snapResponseSchema, payloadSchema, } from "./schemas.js";
4
- export { validateSnapResponse, } from "./validator.js";
5
- export { createDefaultDataStore, createInMemoryDataStore, } from "./dataStore.js";
6
- export { useMiddleware } from "./middleware.js";
4
+ export { validateSnapResponse } from "./validator.js";
@@ -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 SnapPreviewAccentContext = createContext(null);
5
- export function SnapPreviewAccentProvider({ pageAccent, children, }) {
6
- return (_jsx(SnapPreviewAccentContext.Provider, { value: { pageAccent }, children: children }));
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(SnapPreviewAccentContext)?.pageAccent;
9
+ return useContext(SnapPreviewContext)?.pageAccent;
10
+ }
11
+ export function useSnapAppearance() {
12
+ return useContext(SnapPreviewContext)?.appearance ?? "dark";
10
13
  }
@@ -15,6 +15,8 @@ import { SnapStack } from "./components/stack.js";
15
15
  import { SnapSwitch } from "./components/switch.js";
16
16
  import { SnapText } from "./components/text.js";
17
17
  import { SnapToggleGroup } from "./components/toggle-group.js";
18
+ import { SnapBarChart } from "./components/bar-chart.js";
19
+ import { SnapCellGrid } from "./components/cell-grid.js";
18
20
  /**
19
21
  * Maps snap json-render catalog types to React components.
20
22
  * Keys match the snap wire-format `type` strings exactly.
@@ -34,4 +36,6 @@ export const SnapCatalogView = createRenderer(snapJsonRenderCatalog, {
34
36
  switch: SnapSwitch,
35
37
  text: SnapText,
36
38
  toggle_group: SnapToggleGroup,
39
+ bar_chart: SnapBarChart,
40
+ cell_grid: SnapCellGrid,
37
41
  });
@@ -1,6 +1,7 @@
1
- export declare function SnapActionButton({ element: { props }, emit, }: {
1
+ export declare function SnapActionButton({ element, emit, }: {
2
2
  element: {
3
3
  props: Record<string, unknown>;
4
+ on?: Record<string, unknown>;
4
5
  };
5
6
  emit: (name: string) => void;
6
7
  }): import("react/jsx-runtime").JSX.Element;
@@ -1,22 +1,41 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useState } from "react";
4
+ import { ExternalLink } from "lucide-react";
3
5
  import { Button } from "@neynar/ui/button";
4
6
  import { cn } from "@neynar/ui/utils";
5
- import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
7
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
6
8
  import { ICON_MAP } from "./icon.js";
7
- const VARIANT_MAP = {
8
- default: "default",
9
- secondary: "secondary",
10
- outline: "outline",
11
- ghost: "ghost",
12
- };
13
- export function SnapActionButton({ element: { props }, emit, }) {
9
+ function isExternalLinkAction(on) {
10
+ if (!on)
11
+ return false;
12
+ const press = on.press;
13
+ if (!press)
14
+ return false;
15
+ return press.action === "open_url";
16
+ }
17
+ export function SnapActionButton({ element, emit, }) {
18
+ const { props } = element;
14
19
  const label = String(props.label ?? "Action");
15
- const variant = VARIANT_MAP[String(props.variant ?? "default")] ?? "default";
20
+ const variant = String(props.variant ?? "secondary");
21
+ const isPrimary = variant === "primary";
16
22
  const iconName = props.icon ? String(props.icon) : undefined;
17
- const accentStyle = useSnapAccentScopeStyle();
23
+ const colors = useSnapColors();
24
+ const [hovered, setHovered] = useState(false);
18
25
  const Icon = iconName ? ICON_MAP[iconName] : undefined;
19
- return (_jsx("div", { className: "w-full min-w-0 flex-1", style: accentStyle, children: _jsxs(Button, { type: "button", variant: variant, className: cn("w-full gap-2", variant === "default" &&
20
- "hover:!bg-[var(--snap-action-primary-hover)]", variant !== "default" &&
21
- "hover:!bg-[var(--snap-action-outline-hover)]"), onClick: () => emit("press"), children: [Icon && _jsx(Icon, { size: 16 }), label] }) }));
26
+ const showExternalIcon = isExternalLinkAction(element.on);
27
+ const style = isPrimary
28
+ ? {
29
+ backgroundColor: hovered ? colors.accentHover : colors.accent,
30
+ color: colors.accentFg,
31
+ borderColor: "transparent",
32
+ }
33
+ : {
34
+ backgroundColor: hovered
35
+ ? `color-mix(in srgb, ${colors.accent} 15%, transparent)`
36
+ : colors.muted,
37
+ color: colors.text,
38
+ borderColor: "transparent",
39
+ };
40
+ return (_jsx("div", { className: "w-full min-w-0 flex-1", children: _jsxs(Button, { type: "button", variant: isPrimary ? "default" : "secondary", className: cn("w-full gap-2"), style: style, onClick: () => emit("press"), onPointerEnter: () => setHovered(true), onPointerLeave: () => setHovered(false), children: [Icon && _jsx(Icon, { size: 16 }), label, showExternalIcon && (_jsx(ExternalLink, { size: 14, style: { opacity: 0.6 } }))] }) }));
22
41
  }
@@ -1,18 +1,18 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Badge } from "@neynar/ui/badge";
4
- import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
4
+ import { useSnapColors } from "../hooks/use-snap-colors.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
- const accentStyle = useSnapAccentScopeStyle();
11
- const isAccent = !color || color === "accent";
11
+ const colors = useSnapColors();
12
+ const badgeColor = colors.colorHex(color);
12
13
  const Icon = iconName ? ICON_MAP[iconName] : undefined;
13
- return (_jsx("span", { style: isAccent ? accentStyle : undefined, children: _jsxs(Badge, { variant: isAccent ? "default" : "outline", className: "gap-1",
14
- // TODO: fix outline badge border color in @neynar/ui too bright in dark mode
15
- style: !isAccent
16
- ? { borderColor: `var(--snap-color-${color})`, color: `var(--snap-color-${color})` }
17
- : undefined, children: [Icon && _jsx(Icon, { size: 12 }), content] }) }));
14
+ const style = variant === "outline"
15
+ ? { borderColor: badgeColor, color: badgeColor, backgroundColor: "transparent" }
16
+ : { backgroundColor: `${badgeColor}20`, color: badgeColor, borderColor: "transparent" };
17
+ return (_jsxs(Badge, { variant: variant, className: "gap-1", style: style, children: [Icon && _jsx(Icon, { size: 12 }), content] }));
18
18
  }
@@ -0,0 +1,5 @@
1
+ export declare function SnapBarChart({ element: { props }, }: {
2
+ element: {
3
+ props: Record<string, unknown>;
4
+ };
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,26 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
4
+ export function SnapBarChart({ element: { props }, }) {
5
+ const colors = useSnapColors();
6
+ const bars = Array.isArray(props.bars) ? props.bars : [];
7
+ const chartColor = props.color ? String(props.color) : undefined;
8
+ const maxVal = props.max != null
9
+ ? Number(props.max)
10
+ : Math.max(...bars.map((b) => Number(b.value ?? 0)), 1);
11
+ function barFill(bar) {
12
+ if (bar.color)
13
+ return colors.colorHex(bar.color);
14
+ return colors.colorHex(chartColor);
15
+ }
16
+ return (_jsx("div", { className: "flex w-full flex-col gap-2", children: bars.map((bar, i) => {
17
+ const value = Number(bar.value ?? 0);
18
+ const pct = maxVal > 0 ? Math.min(100, (value / maxVal) * 100) : 0;
19
+ const fill = barFill(bar);
20
+ return (_jsxs("div", { className: "flex w-full items-center gap-2", children: [_jsx("span", { className: "w-20 shrink-0 truncate text-right text-xs", style: { color: colors.textMuted }, children: String(bar.label ?? "") }), _jsx("div", { className: "h-2.5 flex-1 overflow-hidden rounded-full", style: { backgroundColor: colors.muted }, children: _jsx("div", { className: "h-full rounded-full transition-all", style: {
21
+ width: `${pct}%`,
22
+ minWidth: pct > 0 ? 4 : 0,
23
+ backgroundColor: fill,
24
+ } }) }), _jsx("span", { className: "w-8 shrink-0 text-xs tabular-nums", style: { color: colors.textMuted }, children: value })] }, i));
25
+ }) }));
26
+ }
@@ -0,0 +1,5 @@
1
+ export declare function SnapCellGrid({ element: { props }, }: {
2
+ element: {
3
+ props: Record<string, unknown>;
4
+ };
5
+ }): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,87 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useStateStore } from "@json-render/react";
4
+ import { cn } from "@neynar/ui/utils";
5
+ import { POST_GRID_TAP_KEY } from "@farcaster/snap";
6
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
7
+ export function SnapCellGrid({ element: { props }, }) {
8
+ const { get, set } = useStateStore();
9
+ const colors = useSnapColors();
10
+ const cols = Number(props.cols ?? 2);
11
+ const rows = Number(props.rows ?? 2);
12
+ const select = String(props.select ?? "off");
13
+ const interactive = select !== "off";
14
+ const isMultiple = select === "multiple";
15
+ const cells = Array.isArray(props.cells) ? props.cells : [];
16
+ const gap = String(props.gap ?? "sm");
17
+ const gapMap = { none: 0, sm: 1, md: 2, lg: 4 };
18
+ const gapPx = gapMap[gap] ?? 1;
19
+ const rowHeight = typeof props.rowHeight === "number" ? props.rowHeight : 28;
20
+ const name = props.name ? String(props.name) : POST_GRID_TAP_KEY;
21
+ const tapPath = `/inputs/${name}`;
22
+ const tapRaw = get(tapPath);
23
+ // Parse selection — single mode: "row,col" string; multi mode: "row,col|row,col|..." string
24
+ const selectedSet = new Set();
25
+ if (typeof tapRaw === "string" && tapRaw.length > 0) {
26
+ for (const part of tapRaw.split("|")) {
27
+ if (part.includes(","))
28
+ selectedSet.add(part);
29
+ }
30
+ }
31
+ const isSelected = (r, c) => selectedSet.has(`${r},${c}`);
32
+ const handleTap = (r, c) => {
33
+ const key = `${r},${c}`;
34
+ if (isMultiple) {
35
+ const next = new Set(selectedSet);
36
+ if (next.has(key))
37
+ next.delete(key);
38
+ else
39
+ next.add(key);
40
+ set(tapPath, [...next].join("|"));
41
+ }
42
+ else {
43
+ set(tapPath, key);
44
+ }
45
+ };
46
+ const cellMap = new Map();
47
+ for (const c of cells) {
48
+ cellMap.set(`${Number(c.row)},${Number(c.col)}`, {
49
+ color: c.color,
50
+ content: c.content != null ? String(c.content) : undefined,
51
+ });
52
+ }
53
+ const cellEls = [];
54
+ for (let r = 0; r < rows; r++) {
55
+ for (let c = 0; c < cols; c++) {
56
+ const cell = cellMap.get(`${r},${c}`);
57
+ const selected = interactive && isSelected(r, c);
58
+ const bg = cell?.color ? colors.colorHex(cell.color) : "transparent";
59
+ cellEls.push(_jsx("div", { role: interactive ? "button" : undefined, tabIndex: interactive ? 0 : undefined, onClick: interactive ? () => handleTap(r, c) : undefined, onKeyDown: interactive
60
+ ? (e) => {
61
+ if (e.key === "Enter" || e.key === " ") {
62
+ e.preventDefault();
63
+ handleTap(r, c);
64
+ }
65
+ }
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,
68
+ background: bg,
69
+ boxShadow: selected
70
+ ? `inset 0 0 0 1px ${colors.mode === "dark" ? "#000" : "#fff"}, inset 0 0 0 2px ${colors.mode === "dark" ? "#fff" : "#000"}`
71
+ : undefined,
72
+ }, children: cell?.content ?? "" }, `${r}-${c}`));
73
+ }
74
+ }
75
+ const selectionLabel = interactive && selectedSet.size > 0
76
+ ? `inputs.${name}: ${[...selectedSet].join(isMultiple ? " | " : "")}`
77
+ : null;
78
+ return (_jsxs("div", { children: [_jsx("div", { style: {
79
+ display: "grid",
80
+ width: "100%",
81
+ gridTemplateColumns: `repeat(${cols}, 1fr)`,
82
+ gap: gapPx,
83
+ padding: 4,
84
+ borderRadius: 8,
85
+ backgroundColor: colors.muted,
86
+ }, children: cellEls }), selectionLabel && (_jsx("div", { className: "mt-1.5 truncate text-xs font-mono", style: { color: colors.textMuted }, children: selectionLabel }))] }));
87
+ }
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { ArrowRight, ArrowLeft, ExternalLink, ChevronRight, Check, X, AlertTriangle, Info, Clock, Heart, MessageCircle, Repeat, Share, User, Users, Star, Trophy, Zap, Flame, Gift, ImageIcon, Play, Pause, Wallet, Coins, Plus, Minus, RefreshCw, Bookmark, ThumbsUp, ThumbsDown, TrendingUp, TrendingDown, } from "lucide-react";
4
- import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
4
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
5
5
  export const ICON_MAP = {
6
6
  "arrow-right": ArrowRight,
7
7
  "arrow-left": ArrowLeft,
@@ -45,16 +45,10 @@ export function SnapIcon({ element: { props }, }) {
45
45
  const name = String(props.name ?? "info");
46
46
  const size = SIZE_PX[String(props.size ?? "md")] ?? 20;
47
47
  const color = props.color ? String(props.color) : undefined;
48
- const accentStyle = useSnapAccentScopeStyle();
48
+ const colors = useSnapColors();
49
49
  const Icon = ICON_MAP[name];
50
50
  if (!Icon)
51
51
  return null;
52
- const isAccent = !color || color === "accent";
53
- return (_jsx("span", { style: {
54
- display: "inline-flex",
55
- alignItems: "center",
56
- ...(isAccent ? accentStyle : {}),
57
- }, children: _jsx(Icon, { size: size, style: isAccent
58
- ? { color: "var(--snap-accent, currentColor)" }
59
- : { color: `var(--snap-color-${color}, currentColor)` } }) }));
52
+ const iconColor = colors.colorHex(color);
53
+ return (_jsx("span", { style: { display: "inline-flex", alignItems: "center" }, children: _jsx(Icon, { size: size, style: { color: iconColor } }) }));
60
54
  }
@@ -4,15 +4,21 @@ import { useId } from "react";
4
4
  import { useStateStore } from "@json-render/react";
5
5
  import { Input } from "@neynar/ui/input";
6
6
  import { Label } from "@neynar/ui/label";
7
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
7
8
  export function SnapInput({ element: { props }, }) {
8
- const id = useId();
9
9
  const { get, set } = useStateStore();
10
+ const colors = useSnapColors();
10
11
  const name = String(props.name ?? "input");
11
- const path = `/inputs/${name}`;
12
+ const type = String(props.type ?? "text");
12
13
  const label = props.label ? String(props.label) : undefined;
13
14
  const placeholder = props.placeholder ? String(props.placeholder) : undefined;
14
- const maxLength = typeof props.maxLength === "number" ? props.maxLength : undefined;
15
- const raw = get(path);
16
- const value = typeof raw === "string" ? raw : "";
17
- return (_jsxs("div", { className: "w-full space-y-1.5", children: [label && _jsx(Label, { htmlFor: id, children: label }), _jsx(Input, { id: id, value: value, onChange: (e) => set(path, e.target.value), placeholder: placeholder, maxLength: maxLength })] }));
15
+ const maxLength = props.maxLength ? Number(props.maxLength) : undefined;
16
+ const path = `/inputs/${name}`;
17
+ const value = get(path) ?? (props.defaultValue != null ? String(props.defaultValue) : "");
18
+ const id = useId();
19
+ return (_jsxs("div", { className: "w-full space-y-1.5", children: [label && (_jsx(Label, { htmlFor: id, style: { color: colors.text }, children: label })), _jsx(Input, { id: id, type: type === "number" ? "number" : "text", placeholder: placeholder, maxLength: maxLength, value: value, onChange: (e) => set(path, e.target.value), style: {
20
+ backgroundColor: colors.inputBg,
21
+ borderColor: colors.inputBorder,
22
+ color: colors.text,
23
+ } })] }));
18
24
  }
@@ -2,6 +2,7 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Children, Fragment } from "react";
4
4
  import { cn } from "@neynar/ui/utils";
5
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
5
6
  const GAP_MAP = {
6
7
  none: "gap-0",
7
8
  sm: "gap-1",
@@ -13,5 +14,6 @@ export function SnapItemGroup({ element: { props }, children, }) {
13
14
  const separator = Boolean(props.separator);
14
15
  const gap = GAP_MAP[String(props.gap ?? "sm")] ?? "gap-1";
15
16
  const items = Children.toArray(children);
16
- return (_jsx("div", { className: cn("flex flex-col", border && "rounded-lg border", gap), children: items.map((child, i) => (_jsxs(Fragment, { children: [separator && i > 0 && (_jsx("div", { className: "h-px bg-border" })), child] }, i))) }));
17
+ const colors = useSnapColors();
18
+ return (_jsx("div", { className: cn("flex flex-col", border && "rounded-lg border", gap), style: border ? { borderColor: colors.border } : undefined, children: items.map((child, i) => (_jsxs(Fragment, { children: [separator && i > 0 && (_jsx("div", { className: "h-px", style: { backgroundColor: colors.border } })), child] }, i))) }));
17
19
  }
@@ -1,7 +1,7 @@
1
- import type { ReactNode } from "react";
2
- export declare function SnapItem({ element: { props }, children, }: {
1
+ export declare function SnapItem({ element: { props, children: childIds }, children, }: {
3
2
  element: {
4
3
  props: Record<string, unknown>;
4
+ children?: string[];
5
5
  };
6
- children?: ReactNode;
6
+ children?: React.ReactNode;
7
7
  }): import("react/jsx-runtime").JSX.Element;
@@ -1,9 +1,10 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Item, ItemContent, ItemTitle, ItemDescription, ItemActions, } from "@neynar/ui/item";
4
- export function SnapItem({ element: { props }, children, }) {
4
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
5
+ export function SnapItem({ element: { props, children: childIds }, children, }) {
5
6
  const title = String(props.title ?? "");
6
7
  const description = props.description ? String(props.description) : undefined;
7
- const variant = props.variant ?? "default";
8
- return (_jsxs(Item, { variant: variant, className: `flex-1 py-1.5 px-2.5 ${variant === "muted" ? "!bg-border/20" : ""}`, 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 })] }));
8
+ const colors = useSnapColors();
9
+ return (_jsxs(Item, { className: "flex-1 py-1.5 px-2.5", children: [_jsxs(ItemContent, { className: "gap-0.5", children: [_jsx(ItemTitle, { style: { color: colors.text }, children: title }), description && (_jsx(ItemDescription, { className: "mt-0", style: { color: colors.textMuted }, children: description }))] }), childIds && childIds.length > 0 && _jsx(ItemActions, { children: children })] }));
9
10
  }
@@ -1,11 +1,11 @@
1
1
  "use client";
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
3
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
4
4
  export function SnapProgress({ element: { props }, }) {
5
- const accentStyle = useSnapAccentScopeStyle();
5
+ const colors = useSnapColors();
6
6
  const value = Number(props.value ?? 0);
7
7
  const max = Math.max(1, Number(props.max ?? 100));
8
8
  const percent = Math.min(100, Math.max(0, (value / max) * 100));
9
9
  const label = props.label ? String(props.label) : null;
10
- return (_jsxs("div", { className: "flex w-full flex-1 flex-col gap-1", style: accentStyle, children: [label && (_jsx("span", { className: "text-muted-foreground text-xs", children: label })), _jsx("div", { className: "bg-muted h-2.5 w-full overflow-hidden rounded-full", children: _jsx("div", { className: "h-full rounded-full bg-primary transition-all", style: { width: `${percent}%` } }) })] }));
10
+ return (_jsxs("div", { className: "flex w-full flex-1 flex-col gap-1", children: [label && (_jsx("span", { className: "text-xs", style: { color: colors.textMuted }, children: label })), _jsx("div", { className: "h-2.5 w-full overflow-hidden rounded-full", style: { backgroundColor: colors.muted }, children: _jsx("div", { className: "h-full rounded-full transition-all", style: { width: `${percent}%`, backgroundColor: colors.accent } }) })] }));
11
11
  }
@@ -1,7 +1,9 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { Separator } from "@neynar/ui/separator";
4
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
4
5
  export function SnapSeparator({ element: { props }, }) {
5
6
  const orientation = props.orientation ?? "horizontal";
6
- return _jsx(Separator, { orientation: orientation });
7
+ const colors = useSnapColors();
8
+ return (_jsx(Separator, { orientation: orientation, style: { backgroundColor: colors.border } }));
7
9
  }
@@ -2,20 +2,25 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useStateStore } from "@json-render/react";
4
4
  import { Label } from "@neynar/ui/label";
5
- import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
6
- // TODO: switch back to @neynar/ui/slider once Base UI fixes the inline
7
- // <script> tag that triggers a React console warning on client render.
5
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
8
6
  export function SnapSlider({ element: { props }, }) {
9
7
  const { get, set } = useStateStore();
10
- const accentStyle = useSnapAccentScopeStyle();
8
+ const colors = useSnapColors();
11
9
  const name = String(props.name ?? "slider");
12
- const path = `/inputs/${name}`;
13
- const label = props.label ? String(props.label) : undefined;
14
10
  const min = Number(props.min ?? 0);
15
11
  const max = Number(props.max ?? 100);
16
- const step = props.step != null ? Number(props.step) : 1;
17
- const fallback = props.defaultValue != null ? Number(props.defaultValue) : (min + max) / 2;
12
+ const step = Number(props.step ?? 1);
13
+ const label = props.label ? String(props.label) : undefined;
14
+ const showValue = props.showValue === true;
15
+ const path = `/inputs/${name}`;
18
16
  const raw = get(path);
19
- const value = raw === undefined || raw === null ? fallback : Number(raw);
20
- return (_jsxs("div", { className: "flex w-full flex-col gap-1.5", style: accentStyle, children: [label && _jsx(Label, { children: label }), _jsx("input", { type: "range", min: min, max: max, step: step, value: value, onChange: (e) => set(path, Number(e.target.value)), className: "w-full h-2.5 rounded-full appearance-none bg-muted cursor-pointer", style: { accentColor: "var(--primary)" } })] }));
17
+ const value = raw !== undefined
18
+ ? Number(raw)
19
+ : props.defaultValue !== undefined
20
+ ? Number(props.defaultValue)
21
+ : (min + max) / 2;
22
+ return (_jsxs("div", { className: "flex w-full flex-col gap-1.5", children: [label && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { style: { color: colors.text }, children: label }), showValue && (_jsx("span", { style: { color: colors.textMuted, fontSize: 13, lineHeight: "18px" }, children: Math.round(value) }))] })), _jsx("input", { type: "range", min: min, max: max, step: step, value: value, onChange: (e) => set(path, Number(e.target.value)), className: "w-full h-2.5 rounded-full appearance-none cursor-pointer", style: {
23
+ backgroundColor: colors.muted,
24
+ accentColor: colors.accent,
25
+ } })] }));
21
26
  }
@@ -2,22 +2,20 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { useId } from "react";
4
4
  import { useStateStore } from "@json-render/react";
5
- import { Label } from "@neynar/ui/label";
6
5
  import { Switch } from "@neynar/ui/switch";
7
- import { useColorMode } from "@neynar/ui/color-mode";
8
- import { cn } from "@neynar/ui/utils";
9
- import { useSnapAccentScopeStyle } from "../hooks/use-snap-accent.js";
6
+ import { Label } from "@neynar/ui/label";
7
+ import { useSnapColors } from "../hooks/use-snap-colors.js";
10
8
  export function SnapSwitch({ element: { props }, }) {
11
- const id = useId();
12
9
  const { get, set } = useStateStore();
13
- const { mode } = useColorMode();
14
- const accentStyle = useSnapAccentScopeStyle();
10
+ const colors = useSnapColors();
15
11
  const name = String(props.name ?? "switch");
16
- const path = `/inputs/${name}`;
17
12
  const label = props.label ? String(props.label) : undefined;
18
- const fallback = Boolean(props.defaultChecked ?? false);
13
+ const path = `/inputs/${name}`;
19
14
  const raw = get(path);
20
- const checked = raw === undefined || raw === null ? fallback : Boolean(raw);
21
- return (_jsxs("div", { className: "flex items-center justify-between gap-3", children: [label && (_jsx(Label, { htmlFor: id, className: "text-foreground font-normal", children: label })), _jsx(Switch, { id: id, checked: checked, onCheckedChange: (v) => set(path, v), style: accentStyle, className: cn(mode === "light" &&
22
- "data-unchecked:!bg-border data-unchecked:!border-(--input-border)") })] }));
15
+ const checked = raw !== undefined ? Boolean(raw) : Boolean(props.defaultChecked);
16
+ const id = useId();
17
+ return (_jsxs("div", { className: "flex items-center justify-between gap-3", children: [label && (_jsx(Label, { htmlFor: id, className: "font-normal", style: { color: colors.text }, children: label })), _jsx(Switch, { id: id, checked: checked, onCheckedChange: (v) => set(path, v), style: {
18
+ backgroundColor: checked ? colors.accent : colors.muted,
19
+ borderColor: checked ? colors.accent : colors.inputBorder,
20
+ } })] }));
23
21
  }