@vetala/vetala 0.1.0-beta

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.

Potentially problematic release.


This version of @vetala/vetala might be problematic. Click here for more details.

Files changed (271) hide show
  1. package/CONTRIBUTING.md +77 -0
  2. package/LICENSE +184 -0
  3. package/README.md +136 -0
  4. package/THIRD_PARTY_LICENSES.md +17 -0
  5. package/dist/src/agent.d.ts +30 -0
  6. package/dist/src/agent.js +216 -0
  7. package/dist/src/agent.js.map +1 -0
  8. package/dist/src/approvals.d.ts +18 -0
  9. package/dist/src/approvals.js +81 -0
  10. package/dist/src/approvals.js.map +1 -0
  11. package/dist/src/cli.d.ts +2 -0
  12. package/dist/src/cli.js +87 -0
  13. package/dist/src/cli.js.map +1 -0
  14. package/dist/src/config.d.ts +12 -0
  15. package/dist/src/config.js +183 -0
  16. package/dist/src/config.js.map +1 -0
  17. package/dist/src/context-memory.d.ts +7 -0
  18. package/dist/src/context-memory.js +96 -0
  19. package/dist/src/context-memory.js.map +1 -0
  20. package/dist/src/ink/command-suggestions.d.ts +7 -0
  21. package/dist/src/ink/command-suggestions.js +179 -0
  22. package/dist/src/ink/command-suggestions.js.map +1 -0
  23. package/dist/src/ink/ink-terminal-ui.d.ts +36 -0
  24. package/dist/src/ink/ink-terminal-ui.js +79 -0
  25. package/dist/src/ink/ink-terminal-ui.js.map +1 -0
  26. package/dist/src/ink/repl-app.d.ts +9 -0
  27. package/dist/src/ink/repl-app.js +789 -0
  28. package/dist/src/ink/repl-app.js.map +1 -0
  29. package/dist/src/ink/transcript-cards.d.ts +6 -0
  30. package/dist/src/ink/transcript-cards.js +24 -0
  31. package/dist/src/ink/transcript-cards.js.map +1 -0
  32. package/dist/src/path-policy.d.ts +11 -0
  33. package/dist/src/path-policy.js +67 -0
  34. package/dist/src/path-policy.js.map +1 -0
  35. package/dist/src/process-utils.d.ts +13 -0
  36. package/dist/src/process-utils.js +52 -0
  37. package/dist/src/process-utils.js.map +1 -0
  38. package/dist/src/repl.d.ts +9 -0
  39. package/dist/src/repl.js +13 -0
  40. package/dist/src/repl.js.map +1 -0
  41. package/dist/src/sarvam/client.d.ts +15 -0
  42. package/dist/src/sarvam/client.js +208 -0
  43. package/dist/src/sarvam/client.js.map +1 -0
  44. package/dist/src/sarvam/models.d.ts +2 -0
  45. package/dist/src/sarvam/models.js +7 -0
  46. package/dist/src/sarvam/models.js.map +1 -0
  47. package/dist/src/search-provider.d.ts +6 -0
  48. package/dist/src/search-provider.js +8 -0
  49. package/dist/src/search-provider.js.map +1 -0
  50. package/dist/src/session-store.d.ts +19 -0
  51. package/dist/src/session-store.js +318 -0
  52. package/dist/src/session-store.js.map +1 -0
  53. package/dist/src/skills/runtime.d.ts +26 -0
  54. package/dist/src/skills/runtime.js +317 -0
  55. package/dist/src/skills/runtime.js.map +1 -0
  56. package/dist/src/skills/types.d.ts +25 -0
  57. package/dist/src/skills/types.js +2 -0
  58. package/dist/src/skills/types.js.map +1 -0
  59. package/dist/src/terminal-ui.d.ts +29 -0
  60. package/dist/src/terminal-ui.js +236 -0
  61. package/dist/src/terminal-ui.js.map +1 -0
  62. package/dist/src/tools/filesystem.d.ts +2 -0
  63. package/dist/src/tools/filesystem.js +622 -0
  64. package/dist/src/tools/filesystem.js.map +1 -0
  65. package/dist/src/tools/git.d.ts +2 -0
  66. package/dist/src/tools/git.js +326 -0
  67. package/dist/src/tools/git.js.map +1 -0
  68. package/dist/src/tools/index.d.ts +6 -0
  69. package/dist/src/tools/index.js +21 -0
  70. package/dist/src/tools/index.js.map +1 -0
  71. package/dist/src/tools/registry.d.ts +15 -0
  72. package/dist/src/tools/registry.js +59 -0
  73. package/dist/src/tools/registry.js.map +1 -0
  74. package/dist/src/tools/shell.d.ts +2 -0
  75. package/dist/src/tools/shell.js +97 -0
  76. package/dist/src/tools/shell.js.map +1 -0
  77. package/dist/src/tools/skill.d.ts +3 -0
  78. package/dist/src/tools/skill.js +130 -0
  79. package/dist/src/tools/skill.js.map +1 -0
  80. package/dist/src/tools/web.d.ts +3 -0
  81. package/dist/src/tools/web.js +144 -0
  82. package/dist/src/tools/web.js.map +1 -0
  83. package/dist/src/types.d.ts +236 -0
  84. package/dist/src/types.js +2 -0
  85. package/dist/src/types.js.map +1 -0
  86. package/dist/src/workspace-trust.d.ts +3 -0
  87. package/dist/src/workspace-trust.js +31 -0
  88. package/dist/src/workspace-trust.js.map +1 -0
  89. package/dist/src/xdg.d.ts +9 -0
  90. package/dist/src/xdg.js +77 -0
  91. package/dist/src/xdg.js.map +1 -0
  92. package/package.json +57 -0
  93. package/skill/agents-md-generator/SKILL.md +75 -0
  94. package/skill/agents-md-generator/references/agents_md_template.md +160 -0
  95. package/skill/agents-md-generator/references/loc_measurement.md +67 -0
  96. package/skill/agents-md-generator/references/monorepo_detection.md +78 -0
  97. package/skill/agents-md-generator/references/monorepo_strategy.md +60 -0
  98. package/skill/agents-md-generator/references/read_only_commands.md +151 -0
  99. package/skill/agents-md-generator/references/update_strategy.md +160 -0
  100. package/skill/agents-md-generator/references/working_agreements.md +53 -0
  101. package/skill/biz-opportunity-scout/SKILL.md +53 -0
  102. package/skill/biz-opportunity-scout/references/competitive_analysis.md +84 -0
  103. package/skill/biz-opportunity-scout/references/market_sizing.md +68 -0
  104. package/skill/biz-opportunity-scout/references/pmf_indicators.md +94 -0
  105. package/skill/biz-opportunity-scout/references/report_template.md +243 -0
  106. package/skill/biz-opportunity-scout/references/unit_economics.md +97 -0
  107. package/skill/code-review/SKILL.md +86 -0
  108. package/skill/code-review/references/change_analysis.md +116 -0
  109. package/skill/code-review/references/git_operations.md +115 -0
  110. package/skill/code-review/references/impact_detection.md +149 -0
  111. package/skill/code-review/references/output_format.md +137 -0
  112. package/skill/code-review/references/severity_criteria.md +100 -0
  113. package/skill/code-security-audit/SKILL.md +123 -0
  114. package/skill/code-security-audit/references/audit_process.md +277 -0
  115. package/skill/code-security-audit/references/remediation_patterns.md +599 -0
  116. package/skill/code-security-audit/references/report_format.md +391 -0
  117. package/skill/code-security-audit/references/security_domains.md +830 -0
  118. package/skill/code-security-audit/references/vulnerability_patterns.md +813 -0
  119. package/skill/composition-patterns/SKILL.md +83 -0
  120. package/skill/composition-patterns/rules/architecture-avoid-boolean-props.md +100 -0
  121. package/skill/composition-patterns/rules/architecture-compound-components.md +112 -0
  122. package/skill/composition-patterns/rules/patterns-children-over-render-props.md +87 -0
  123. package/skill/composition-patterns/rules/patterns-explicit-variants.md +100 -0
  124. package/skill/composition-patterns/rules/react19-no-forwardref.md +42 -0
  125. package/skill/composition-patterns/rules/state-context-interface.md +191 -0
  126. package/skill/composition-patterns/rules/state-decouple-implementation.md +113 -0
  127. package/skill/composition-patterns/rules/state-lift-state.md +125 -0
  128. package/skill/deploy-to-vercel/SKILL.md +293 -0
  129. package/skill/deploy-to-vercel/resources/deploy-sandbox.sh +301 -0
  130. package/skill/deploy-to-vercel/resources/deploy.sh +301 -0
  131. package/skill/doc/SKILL_GUIDELINES.md +138 -0
  132. package/skill/git-workflow/SKILL.md +94 -0
  133. package/skill/git-workflow/references/advanced-git.md +632 -0
  134. package/skill/git-workflow/references/branching-strategies.md +344 -0
  135. package/skill/git-workflow/references/ci-cd-integration.md +683 -0
  136. package/skill/git-workflow/references/code-quality-tools.md +351 -0
  137. package/skill/git-workflow/references/commit-conventions.md +439 -0
  138. package/skill/git-workflow/references/github-releases.md +288 -0
  139. package/skill/git-workflow/references/pull-request-workflow.md +773 -0
  140. package/skill/git-workflow/scripts/verify-git-workflow.sh +263 -0
  141. package/skill/jetbrains-vmoptions/SKILL.md +51 -0
  142. package/skill/jetbrains-vmoptions/references/common-options.md +357 -0
  143. package/skill/jetbrains-vmoptions/references/gc-options.md +350 -0
  144. package/skill/jetbrains-vmoptions/references/memory-options.md +339 -0
  145. package/skill/jetbrains-vmoptions/references/prerequisite-check.md +65 -0
  146. package/skill/kysely-converter/SKILL.md +62 -0
  147. package/skill/kysely-converter/references/delete.md +323 -0
  148. package/skill/kysely-converter/references/insert.md +386 -0
  149. package/skill/kysely-converter/references/operators.md +331 -0
  150. package/skill/kysely-converter/references/select.md +1000 -0
  151. package/skill/kysely-converter/references/update.md +349 -0
  152. package/skill/kysely-converter/references/window_function.md +537 -0
  153. package/skill/react-best-practices/SKILL.md +131 -0
  154. package/skill/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  155. package/skill/react-best-practices/rules/advanced-init-once.md +42 -0
  156. package/skill/react-best-practices/rules/advanced-use-latest.md +39 -0
  157. package/skill/react-best-practices/rules/async-api-routes.md +38 -0
  158. package/skill/react-best-practices/rules/async-defer-await.md +80 -0
  159. package/skill/react-best-practices/rules/async-dependencies.md +51 -0
  160. package/skill/react-best-practices/rules/async-parallel.md +28 -0
  161. package/skill/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  162. package/skill/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  163. package/skill/react-best-practices/rules/bundle-conditional.md +31 -0
  164. package/skill/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  165. package/skill/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  166. package/skill/react-best-practices/rules/bundle-preload.md +50 -0
  167. package/skill/react-best-practices/rules/client-event-listeners.md +74 -0
  168. package/skill/react-best-practices/rules/client-localstorage-schema.md +71 -0
  169. package/skill/react-best-practices/rules/client-passive-event-listeners.md +48 -0
  170. package/skill/react-best-practices/rules/client-swr-dedup.md +56 -0
  171. package/skill/react-best-practices/rules/js-batch-dom-css.md +107 -0
  172. package/skill/react-best-practices/rules/js-cache-function-results.md +80 -0
  173. package/skill/react-best-practices/rules/js-cache-property-access.md +28 -0
  174. package/skill/react-best-practices/rules/js-cache-storage.md +70 -0
  175. package/skill/react-best-practices/rules/js-combine-iterations.md +32 -0
  176. package/skill/react-best-practices/rules/js-early-exit.md +50 -0
  177. package/skill/react-best-practices/rules/js-hoist-regexp.md +45 -0
  178. package/skill/react-best-practices/rules/js-index-maps.md +37 -0
  179. package/skill/react-best-practices/rules/js-length-check-first.md +49 -0
  180. package/skill/react-best-practices/rules/js-min-max-loop.md +82 -0
  181. package/skill/react-best-practices/rules/js-set-map-lookups.md +24 -0
  182. package/skill/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  183. package/skill/react-best-practices/rules/rendering-activity.md +26 -0
  184. package/skill/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  185. package/skill/react-best-practices/rules/rendering-conditional-render.md +40 -0
  186. package/skill/react-best-practices/rules/rendering-content-visibility.md +38 -0
  187. package/skill/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  188. package/skill/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  189. package/skill/react-best-practices/rules/rendering-hydration-suppress-warning.md +30 -0
  190. package/skill/react-best-practices/rules/rendering-svg-precision.md +28 -0
  191. package/skill/react-best-practices/rules/rendering-usetransition-loading.md +75 -0
  192. package/skill/react-best-practices/rules/rerender-defer-reads.md +39 -0
  193. package/skill/react-best-practices/rules/rerender-dependencies.md +45 -0
  194. package/skill/react-best-practices/rules/rerender-derived-state-no-effect.md +40 -0
  195. package/skill/react-best-practices/rules/rerender-derived-state.md +29 -0
  196. package/skill/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  197. package/skill/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  198. package/skill/react-best-practices/rules/rerender-memo-with-default-value.md +38 -0
  199. package/skill/react-best-practices/rules/rerender-memo.md +44 -0
  200. package/skill/react-best-practices/rules/rerender-move-effect-to-event.md +45 -0
  201. package/skill/react-best-practices/rules/rerender-simple-expression-in-memo.md +35 -0
  202. package/skill/react-best-practices/rules/rerender-transitions.md +40 -0
  203. package/skill/react-best-practices/rules/rerender-use-ref-transient-values.md +73 -0
  204. package/skill/react-best-practices/rules/server-after-nonblocking.md +73 -0
  205. package/skill/react-best-practices/rules/server-auth-actions.md +96 -0
  206. package/skill/react-best-practices/rules/server-cache-lru.md +41 -0
  207. package/skill/react-best-practices/rules/server-cache-react.md +76 -0
  208. package/skill/react-best-practices/rules/server-dedup-props.md +65 -0
  209. package/skill/react-best-practices/rules/server-hoist-static-io.md +142 -0
  210. package/skill/react-best-practices/rules/server-parallel-fetching.md +83 -0
  211. package/skill/react-best-practices/rules/server-serialization.md +38 -0
  212. package/skill/react-native-skills/SKILL.md +115 -0
  213. package/skill/react-native-skills/rules/animation-derived-value.md +53 -0
  214. package/skill/react-native-skills/rules/animation-gesture-detector-press.md +95 -0
  215. package/skill/react-native-skills/rules/animation-gpu-properties.md +65 -0
  216. package/skill/react-native-skills/rules/design-system-compound-components.md +66 -0
  217. package/skill/react-native-skills/rules/fonts-config-plugin.md +71 -0
  218. package/skill/react-native-skills/rules/imports-design-system-folder.md +68 -0
  219. package/skill/react-native-skills/rules/js-hoist-intl.md +61 -0
  220. package/skill/react-native-skills/rules/list-performance-callbacks.md +44 -0
  221. package/skill/react-native-skills/rules/list-performance-function-references.md +132 -0
  222. package/skill/react-native-skills/rules/list-performance-images.md +53 -0
  223. package/skill/react-native-skills/rules/list-performance-inline-objects.md +97 -0
  224. package/skill/react-native-skills/rules/list-performance-item-expensive.md +94 -0
  225. package/skill/react-native-skills/rules/list-performance-item-memo.md +82 -0
  226. package/skill/react-native-skills/rules/list-performance-item-types.md +104 -0
  227. package/skill/react-native-skills/rules/list-performance-virtualize.md +67 -0
  228. package/skill/react-native-skills/rules/monorepo-native-deps-in-app.md +46 -0
  229. package/skill/react-native-skills/rules/monorepo-single-dependency-versions.md +63 -0
  230. package/skill/react-native-skills/rules/navigation-native-navigators.md +188 -0
  231. package/skill/react-native-skills/rules/react-compiler-destructure-functions.md +50 -0
  232. package/skill/react-native-skills/rules/react-compiler-reanimated-shared-values.md +48 -0
  233. package/skill/react-native-skills/rules/react-state-dispatcher.md +91 -0
  234. package/skill/react-native-skills/rules/react-state-fallback.md +56 -0
  235. package/skill/react-native-skills/rules/react-state-minimize.md +65 -0
  236. package/skill/react-native-skills/rules/rendering-no-falsy-and.md +74 -0
  237. package/skill/react-native-skills/rules/rendering-text-in-text-component.md +36 -0
  238. package/skill/react-native-skills/rules/scroll-position-no-state.md +82 -0
  239. package/skill/react-native-skills/rules/state-ground-truth.md +80 -0
  240. package/skill/react-native-skills/rules/ui-expo-image.md +66 -0
  241. package/skill/react-native-skills/rules/ui-image-gallery.md +104 -0
  242. package/skill/react-native-skills/rules/ui-measure-views.md +78 -0
  243. package/skill/react-native-skills/rules/ui-menus.md +174 -0
  244. package/skill/react-native-skills/rules/ui-native-modals.md +77 -0
  245. package/skill/react-native-skills/rules/ui-pressable.md +61 -0
  246. package/skill/react-native-skills/rules/ui-safe-area-scroll.md +65 -0
  247. package/skill/react-native-skills/rules/ui-scrollview-content-inset.md +45 -0
  248. package/skill/react-native-skills/rules/ui-styling.md +87 -0
  249. package/skill/react-vite-guide/SKILL.md +101 -0
  250. package/skill/react-vite-guide/references/composition-patterns.md +709 -0
  251. package/skill/react-vite-guide/references/performance-optimization.md +1222 -0
  252. package/skill/react-vite-guide/references/vite-specific.md +385 -0
  253. package/skill/react-vite-guide/references/web-interface.md +146 -0
  254. package/skill/skill-maker/SKILL.md +52 -0
  255. package/skill/skill-maker/references/content_spec.md +67 -0
  256. package/skill/skill-maker/references/frontmatter_spec.md +96 -0
  257. package/skill/skill-maker/references/input_validation.md +90 -0
  258. package/skill/skill-maker/references/skill_structure.md +74 -0
  259. package/skill/system-prompt-creator/SKILL.md +50 -0
  260. package/skill/system-prompt-creator/references/data_format_selection.md +135 -0
  261. package/skill/system-prompt-creator/references/multi_prompt_architecture.md +386 -0
  262. package/skill/system-prompt-creator/references/prompt_structure.md +140 -0
  263. package/skill/system-prompt-creator/references/quality_criteria.md +83 -0
  264. package/skill/typst-creator/SKILL.md +51 -0
  265. package/skill/typst-creator/references/layout.md +401 -0
  266. package/skill/typst-creator/references/math.md +297 -0
  267. package/skill/typst-creator/references/scripting.md +237 -0
  268. package/skill/typst-creator/references/styling.md +217 -0
  269. package/skill/typst-creator/references/syntax.md +234 -0
  270. package/skill/web-design-guidelines/SKILL.md +35 -0
  271. package/terminal.png +0 -0
@@ -0,0 +1,80 @@
1
+ ---
2
+ title: State Must Represent Ground Truth
3
+ impact: HIGH
4
+ impactDescription: cleaner logic, easier debugging, single source of truth
5
+ tags: state, derived-state, reanimated, hooks
6
+ ---
7
+
8
+ ## State Must Represent Ground Truth
9
+
10
+ State variables—both React `useState` and Reanimated shared values—should
11
+ represent the actual state of something (e.g., `pressed`, `progress`, `isOpen`),
12
+ not derived visual values (e.g., `scale`, `opacity`, `translateY`). Derive
13
+ visual values from state using computation or interpolation.
14
+
15
+ **Incorrect (storing the visual output):**
16
+
17
+ ```tsx
18
+ const scale = useSharedValue(1)
19
+
20
+ const tap = Gesture.Tap()
21
+ .onBegin(() => {
22
+ scale.set(withTiming(0.95))
23
+ })
24
+ .onFinalize(() => {
25
+ scale.set(withTiming(1))
26
+ })
27
+
28
+ const animatedStyle = useAnimatedStyle(() => ({
29
+ transform: [{ scale: scale.get() }],
30
+ }))
31
+ ```
32
+
33
+ **Correct (storing the state, deriving the visual):**
34
+
35
+ ```tsx
36
+ const pressed = useSharedValue(0) // 0 = not pressed, 1 = pressed
37
+
38
+ const tap = Gesture.Tap()
39
+ .onBegin(() => {
40
+ pressed.set(withTiming(1))
41
+ })
42
+ .onFinalize(() => {
43
+ pressed.set(withTiming(0))
44
+ })
45
+
46
+ const animatedStyle = useAnimatedStyle(() => ({
47
+ transform: [{ scale: interpolate(pressed.get(), [0, 1], [1, 0.95]) }],
48
+ }))
49
+ ```
50
+
51
+ **Why this matters:**
52
+
53
+ State variables should represent real "state", not necessarily a desired end
54
+ result.
55
+
56
+ 1. **Single source of truth** — The state (`pressed`) describes what's
57
+ happening; visuals are derived
58
+ 2. **Easier to extend** — Adding opacity, rotation, or other effects just
59
+ requires more interpolations from the same state
60
+ 3. **Debugging** — Inspecting `pressed = 1` is clearer than `scale = 0.95`
61
+ 4. **Reusable logic** — The same `pressed` value can drive multiple visual
62
+ properties
63
+
64
+ **Same principle for React state:**
65
+
66
+ ```tsx
67
+ // Incorrect: storing derived values
68
+ const [isExpanded, setIsExpanded] = useState(false)
69
+ const [height, setHeight] = useState(0)
70
+
71
+ useEffect(() => {
72
+ setHeight(isExpanded ? 200 : 0)
73
+ }, [isExpanded])
74
+
75
+ // Correct: derive from state
76
+ const [isExpanded, setIsExpanded] = useState(false)
77
+ const height = isExpanded ? 200 : 0
78
+ ```
79
+
80
+ State is the minimal truth. Everything else is derived.
@@ -0,0 +1,66 @@
1
+ ---
2
+ title: Use expo-image for Optimized Images
3
+ impact: HIGH
4
+ impactDescription: memory efficiency, caching, blurhash placeholders, progressive loading
5
+ tags: images, performance, expo-image, ui
6
+ ---
7
+
8
+ ## Use expo-image for Optimized Images
9
+
10
+ Use `expo-image` instead of React Native's `Image`. It provides memory-efficient caching, blurhash placeholders, progressive loading, and better performance for lists.
11
+
12
+ **Incorrect (React Native Image):**
13
+
14
+ ```tsx
15
+ import { Image } from 'react-native'
16
+
17
+ function Avatar({ url }: { url: string }) {
18
+ return <Image source={{ uri: url }} style={styles.avatar} />
19
+ }
20
+ ```
21
+
22
+ **Correct (expo-image):**
23
+
24
+ ```tsx
25
+ import { Image } from 'expo-image'
26
+
27
+ function Avatar({ url }: { url: string }) {
28
+ return <Image source={{ uri: url }} style={styles.avatar} />
29
+ }
30
+ ```
31
+
32
+ **With blurhash placeholder:**
33
+
34
+ ```tsx
35
+ <Image
36
+ source={{ uri: url }}
37
+ placeholder={{ blurhash: 'LGF5]+Yk^6#M@-5c,1J5@[or[Q6.' }}
38
+ contentFit="cover"
39
+ transition={200}
40
+ style={styles.image}
41
+ />
42
+ ```
43
+
44
+ **With priority and caching:**
45
+
46
+ ```tsx
47
+ <Image
48
+ source={{ uri: url }}
49
+ priority="high"
50
+ cachePolicy="memory-disk"
51
+ style={styles.hero}
52
+ />
53
+ ```
54
+
55
+ **Key props:**
56
+
57
+ - `placeholder` — Blurhash or thumbnail while loading
58
+ - `contentFit` — `cover`, `contain`, `fill`, `scale-down`
59
+ - `transition` — Fade-in duration (ms)
60
+ - `priority` — `low`, `normal`, `high`
61
+ - `cachePolicy` — `memory`, `disk`, `memory-disk`, `none`
62
+ - `recyclingKey` — Unique key for list recycling
63
+
64
+ For cross-platform (web + native), use `SolitoImage` from `solito/image` which uses `expo-image` under the hood.
65
+
66
+ Reference: [expo-image](https://docs.expo.dev/versions/latest/sdk/image/)
@@ -0,0 +1,104 @@
1
+ ---
2
+ title: Use Galeria for Image Galleries and Lightbox
3
+ impact: MEDIUM
4
+ impactDescription:
5
+ native shared element transitions, pinch-to-zoom, pan-to-close
6
+ tags: images, gallery, lightbox, expo-image, ui
7
+ ---
8
+
9
+ ## Use Galeria for Image Galleries and Lightbox
10
+
11
+ For image galleries with lightbox (tap to fullscreen), use `@nandorojo/galeria`.
12
+ It provides native shared element transitions with pinch-to-zoom, double-tap
13
+ zoom, and pan-to-close. Works with any image component including `expo-image`.
14
+
15
+ **Incorrect (custom modal implementation):**
16
+
17
+ ```tsx
18
+ function ImageGallery({ urls }: { urls: string[] }) {
19
+ const [selected, setSelected] = useState<string | null>(null)
20
+
21
+ return (
22
+ <>
23
+ {urls.map((url) => (
24
+ <Pressable key={url} onPress={() => setSelected(url)}>
25
+ <Image source={{ uri: url }} style={styles.thumbnail} />
26
+ </Pressable>
27
+ ))}
28
+ <Modal visible={!!selected} onRequestClose={() => setSelected(null)}>
29
+ <Image source={{ uri: selected! }} style={styles.fullscreen} />
30
+ </Modal>
31
+ </>
32
+ )
33
+ }
34
+ ```
35
+
36
+ **Correct (Galeria with expo-image):**
37
+
38
+ ```tsx
39
+ import { Galeria } from '@nandorojo/galeria'
40
+ import { Image } from 'expo-image'
41
+
42
+ function ImageGallery({ urls }: { urls: string[] }) {
43
+ return (
44
+ <Galeria urls={urls}>
45
+ {urls.map((url, index) => (
46
+ <Galeria.Image index={index} key={url}>
47
+ <Image source={{ uri: url }} style={styles.thumbnail} />
48
+ </Galeria.Image>
49
+ ))}
50
+ </Galeria>
51
+ )
52
+ }
53
+ ```
54
+
55
+ **Single image:**
56
+
57
+ ```tsx
58
+ import { Galeria } from '@nandorojo/galeria'
59
+ import { Image } from 'expo-image'
60
+
61
+ function Avatar({ url }: { url: string }) {
62
+ return (
63
+ <Galeria urls={[url]}>
64
+ <Galeria.Image>
65
+ <Image source={{ uri: url }} style={styles.avatar} />
66
+ </Galeria.Image>
67
+ </Galeria>
68
+ )
69
+ }
70
+ ```
71
+
72
+ **With low-res thumbnails and high-res fullscreen:**
73
+
74
+ ```tsx
75
+ <Galeria urls={highResUrls}>
76
+ {lowResUrls.map((url, index) => (
77
+ <Galeria.Image index={index} key={url}>
78
+ <Image source={{ uri: url }} style={styles.thumbnail} />
79
+ </Galeria.Image>
80
+ ))}
81
+ </Galeria>
82
+ ```
83
+
84
+ **With FlashList:**
85
+
86
+ ```tsx
87
+ <Galeria urls={urls}>
88
+ <FlashList
89
+ data={urls}
90
+ renderItem={({ item, index }) => (
91
+ <Galeria.Image index={index}>
92
+ <Image source={{ uri: item }} style={styles.thumbnail} />
93
+ </Galeria.Image>
94
+ )}
95
+ numColumns={3}
96
+ estimatedItemSize={100}
97
+ />
98
+ </Galeria>
99
+ ```
100
+
101
+ Works with `expo-image`, `SolitoImage`, `react-native` Image, or any image
102
+ component.
103
+
104
+ Reference: [Galeria](https://github.com/nandorojo/galeria)
@@ -0,0 +1,78 @@
1
+ ---
2
+ title: Measuring View Dimensions
3
+ impact: MEDIUM
4
+ impactDescription: synchronous measurement, avoid unnecessary re-renders
5
+ tags: layout, measurement, onLayout, useLayoutEffect
6
+ ---
7
+
8
+ ## Measuring View Dimensions
9
+
10
+ Use both `useLayoutEffect` (synchronous) and `onLayout` (for updates). The sync
11
+ measurement gives you the initial size immediately; `onLayout` keeps it current
12
+ when the view changes. For non-primitive states, use a dispatch updater to
13
+ compare values and avoid unnecessary re-renders.
14
+
15
+ **Height only:**
16
+
17
+ ```tsx
18
+ import { useLayoutEffect, useRef, useState } from 'react'
19
+ import { View, LayoutChangeEvent } from 'react-native'
20
+
21
+ function MeasuredBox({ children }: { children: React.ReactNode }) {
22
+ const ref = useRef<View>(null)
23
+ const [height, setHeight] = useState<number | undefined>(undefined)
24
+
25
+ useLayoutEffect(() => {
26
+ // Sync measurement on mount (RN 0.82+)
27
+ const rect = ref.current?.getBoundingClientRect()
28
+ if (rect) setHeight(rect.height)
29
+ // Pre-0.82: ref.current?.measure((x, y, w, h) => setHeight(h))
30
+ }, [])
31
+
32
+ const onLayout = (e: LayoutChangeEvent) => {
33
+ setHeight(e.nativeEvent.layout.height)
34
+ }
35
+
36
+ return (
37
+ <View ref={ref} onLayout={onLayout}>
38
+ {children}
39
+ </View>
40
+ )
41
+ }
42
+ ```
43
+
44
+ **Both dimensions:**
45
+
46
+ ```tsx
47
+ import { useLayoutEffect, useRef, useState } from 'react'
48
+ import { View, LayoutChangeEvent } from 'react-native'
49
+
50
+ type Size = { width: number; height: number }
51
+
52
+ function MeasuredBox({ children }: { children: React.ReactNode }) {
53
+ const ref = useRef<View>(null)
54
+ const [size, setSize] = useState<Size | undefined>(undefined)
55
+
56
+ useLayoutEffect(() => {
57
+ const rect = ref.current?.getBoundingClientRect()
58
+ if (rect) setSize({ width: rect.width, height: rect.height })
59
+ }, [])
60
+
61
+ const onLayout = (e: LayoutChangeEvent) => {
62
+ const { width, height } = e.nativeEvent.layout
63
+ setSize((prev) => {
64
+ // for non-primitive states, compare values before firing a re-render
65
+ if (prev?.width === width && prev?.height === height) return prev
66
+ return { width, height }
67
+ })
68
+ }
69
+
70
+ return (
71
+ <View ref={ref} onLayout={onLayout}>
72
+ {children}
73
+ </View>
74
+ )
75
+ }
76
+ ```
77
+
78
+ Use functional setState to compare—don't read state directly in the callback.
@@ -0,0 +1,174 @@
1
+ ---
2
+ title: Use Native Menus for Dropdowns and Context Menus
3
+ impact: HIGH
4
+ impactDescription: native accessibility, platform-consistent UX
5
+ tags: user-interface, menus, context-menus, zeego, accessibility
6
+ ---
7
+
8
+ ## Use Native Menus for Dropdowns and Context Menus
9
+
10
+ Use native platform menus instead of custom JS implementations. Native menus
11
+ provide built-in accessibility, consistent platform UX, and better performance.
12
+ Use [zeego](https://zeego.dev) for cross-platform native menus.
13
+
14
+ **Incorrect (custom JS menu):**
15
+
16
+ ```tsx
17
+ import { useState } from 'react'
18
+ import { View, Pressable, Text } from 'react-native'
19
+
20
+ function MyMenu() {
21
+ const [open, setOpen] = useState(false)
22
+
23
+ return (
24
+ <View>
25
+ <Pressable onPress={() => setOpen(!open)}>
26
+ <Text>Open Menu</Text>
27
+ </Pressable>
28
+ {open && (
29
+ <View style={{ position: 'absolute', top: 40 }}>
30
+ <Pressable onPress={() => console.log('edit')}>
31
+ <Text>Edit</Text>
32
+ </Pressable>
33
+ <Pressable onPress={() => console.log('delete')}>
34
+ <Text>Delete</Text>
35
+ </Pressable>
36
+ </View>
37
+ )}
38
+ </View>
39
+ )
40
+ }
41
+ ```
42
+
43
+ **Correct (native menu with zeego):**
44
+
45
+ ```tsx
46
+ import * as DropdownMenu from 'zeego/dropdown-menu'
47
+
48
+ function MyMenu() {
49
+ return (
50
+ <DropdownMenu.Root>
51
+ <DropdownMenu.Trigger>
52
+ <Pressable>
53
+ <Text>Open Menu</Text>
54
+ </Pressable>
55
+ </DropdownMenu.Trigger>
56
+
57
+ <DropdownMenu.Content>
58
+ <DropdownMenu.Item key='edit' onSelect={() => console.log('edit')}>
59
+ <DropdownMenu.ItemTitle>Edit</DropdownMenu.ItemTitle>
60
+ </DropdownMenu.Item>
61
+
62
+ <DropdownMenu.Item
63
+ key='delete'
64
+ destructive
65
+ onSelect={() => console.log('delete')}
66
+ >
67
+ <DropdownMenu.ItemTitle>Delete</DropdownMenu.ItemTitle>
68
+ </DropdownMenu.Item>
69
+ </DropdownMenu.Content>
70
+ </DropdownMenu.Root>
71
+ )
72
+ }
73
+ ```
74
+
75
+ **Context menu (long-press):**
76
+
77
+ ```tsx
78
+ import * as ContextMenu from 'zeego/context-menu'
79
+
80
+ function MyContextMenu() {
81
+ return (
82
+ <ContextMenu.Root>
83
+ <ContextMenu.Trigger>
84
+ <View style={{ padding: 20 }}>
85
+ <Text>Long press me</Text>
86
+ </View>
87
+ </ContextMenu.Trigger>
88
+
89
+ <ContextMenu.Content>
90
+ <ContextMenu.Item key='copy' onSelect={() => console.log('copy')}>
91
+ <ContextMenu.ItemTitle>Copy</ContextMenu.ItemTitle>
92
+ </ContextMenu.Item>
93
+
94
+ <ContextMenu.Item key='paste' onSelect={() => console.log('paste')}>
95
+ <ContextMenu.ItemTitle>Paste</ContextMenu.ItemTitle>
96
+ </ContextMenu.Item>
97
+ </ContextMenu.Content>
98
+ </ContextMenu.Root>
99
+ )
100
+ }
101
+ ```
102
+
103
+ **Checkbox items:**
104
+
105
+ ```tsx
106
+ import * as DropdownMenu from 'zeego/dropdown-menu'
107
+
108
+ function SettingsMenu() {
109
+ const [notifications, setNotifications] = useState(true)
110
+
111
+ return (
112
+ <DropdownMenu.Root>
113
+ <DropdownMenu.Trigger>
114
+ <Pressable>
115
+ <Text>Settings</Text>
116
+ </Pressable>
117
+ </DropdownMenu.Trigger>
118
+
119
+ <DropdownMenu.Content>
120
+ <DropdownMenu.CheckboxItem
121
+ key='notifications'
122
+ value={notifications}
123
+ onValueChange={() => setNotifications((prev) => !prev)}
124
+ >
125
+ <DropdownMenu.ItemIndicator />
126
+ <DropdownMenu.ItemTitle>Notifications</DropdownMenu.ItemTitle>
127
+ </DropdownMenu.CheckboxItem>
128
+ </DropdownMenu.Content>
129
+ </DropdownMenu.Root>
130
+ )
131
+ }
132
+ ```
133
+
134
+ **Submenus:**
135
+
136
+ ```tsx
137
+ import * as DropdownMenu from 'zeego/dropdown-menu'
138
+
139
+ function MenuWithSubmenu() {
140
+ return (
141
+ <DropdownMenu.Root>
142
+ <DropdownMenu.Trigger>
143
+ <Pressable>
144
+ <Text>Options</Text>
145
+ </Pressable>
146
+ </DropdownMenu.Trigger>
147
+
148
+ <DropdownMenu.Content>
149
+ <DropdownMenu.Item key='home' onSelect={() => console.log('home')}>
150
+ <DropdownMenu.ItemTitle>Home</DropdownMenu.ItemTitle>
151
+ </DropdownMenu.Item>
152
+
153
+ <DropdownMenu.Sub>
154
+ <DropdownMenu.SubTrigger key='more'>
155
+ <DropdownMenu.ItemTitle>More Options</DropdownMenu.ItemTitle>
156
+ </DropdownMenu.SubTrigger>
157
+
158
+ <DropdownMenu.SubContent>
159
+ <DropdownMenu.Item key='settings'>
160
+ <DropdownMenu.ItemTitle>Settings</DropdownMenu.ItemTitle>
161
+ </DropdownMenu.Item>
162
+
163
+ <DropdownMenu.Item key='help'>
164
+ <DropdownMenu.ItemTitle>Help</DropdownMenu.ItemTitle>
165
+ </DropdownMenu.Item>
166
+ </DropdownMenu.SubContent>
167
+ </DropdownMenu.Sub>
168
+ </DropdownMenu.Content>
169
+ </DropdownMenu.Root>
170
+ )
171
+ }
172
+ ```
173
+
174
+ Reference: [Zeego Documentation](https://zeego.dev/components/dropdown-menu)
@@ -0,0 +1,77 @@
1
+ ---
2
+ title: Use Native Modals Over JS-Based Bottom Sheets
3
+ impact: HIGH
4
+ impactDescription: native performance, gestures, accessibility
5
+ tags: modals, bottom-sheet, native, react-navigation
6
+ ---
7
+
8
+ ## Use Native Modals Over JS-Based Bottom Sheets
9
+
10
+ Use native `<Modal>` with `presentationStyle="formSheet"` or React Navigation
11
+ v7's native form sheet instead of JS-based bottom sheet libraries. Native modals
12
+ have built-in gestures, accessibility, and better performance. Rely on native UI
13
+ for low-level primitives.
14
+
15
+ **Incorrect (JS-based bottom sheet):**
16
+
17
+ ```tsx
18
+ import BottomSheet from 'custom-js-bottom-sheet'
19
+
20
+ function MyScreen() {
21
+ const sheetRef = useRef<BottomSheet>(null)
22
+
23
+ return (
24
+ <View style={{ flex: 1 }}>
25
+ <Button onPress={() => sheetRef.current?.expand()} title='Open' />
26
+ <BottomSheet ref={sheetRef} snapPoints={['50%', '90%']}>
27
+ <View>
28
+ <Text>Sheet content</Text>
29
+ </View>
30
+ </BottomSheet>
31
+ </View>
32
+ )
33
+ }
34
+ ```
35
+
36
+ **Correct (native Modal with formSheet):**
37
+
38
+ ```tsx
39
+ import { Modal, View, Text, Button } from 'react-native'
40
+
41
+ function MyScreen() {
42
+ const [visible, setVisible] = useState(false)
43
+
44
+ return (
45
+ <View style={{ flex: 1 }}>
46
+ <Button onPress={() => setVisible(true)} title='Open' />
47
+ <Modal
48
+ visible={visible}
49
+ presentationStyle='formSheet'
50
+ animationType='slide'
51
+ onRequestClose={() => setVisible(false)}
52
+ >
53
+ <View>
54
+ <Text>Sheet content</Text>
55
+ </View>
56
+ </Modal>
57
+ </View>
58
+ )
59
+ }
60
+ ```
61
+
62
+ **Correct (React Navigation v7 native form sheet):**
63
+
64
+ ```tsx
65
+ // In your navigator
66
+ <Stack.Screen
67
+ name='Details'
68
+ component={DetailsScreen}
69
+ options={{
70
+ presentation: 'formSheet',
71
+ sheetAllowedDetents: 'fitToContents',
72
+ }}
73
+ />
74
+ ```
75
+
76
+ Native modals provide swipe-to-dismiss, proper keyboard avoidance, and
77
+ accessibility out of the box.
@@ -0,0 +1,61 @@
1
+ ---
2
+ title: Use Pressable Instead of Touchable Components
3
+ impact: LOW
4
+ impactDescription: modern API, more flexible
5
+ tags: ui, pressable, touchable, gestures
6
+ ---
7
+
8
+ ## Use Pressable Instead of Touchable Components
9
+
10
+ Never use `TouchableOpacity` or `TouchableHighlight`. Use `Pressable` from
11
+ `react-native` or `react-native-gesture-handler` instead.
12
+
13
+ **Incorrect (legacy Touchable components):**
14
+
15
+ ```tsx
16
+ import { TouchableOpacity } from 'react-native'
17
+
18
+ function MyButton({ onPress }: { onPress: () => void }) {
19
+ return (
20
+ <TouchableOpacity onPress={onPress} activeOpacity={0.7}>
21
+ <Text>Press me</Text>
22
+ </TouchableOpacity>
23
+ )
24
+ }
25
+ ```
26
+
27
+ **Correct (Pressable):**
28
+
29
+ ```tsx
30
+ import { Pressable } from 'react-native'
31
+
32
+ function MyButton({ onPress }: { onPress: () => void }) {
33
+ return (
34
+ <Pressable onPress={onPress}>
35
+ <Text>Press me</Text>
36
+ </Pressable>
37
+ )
38
+ }
39
+ ```
40
+
41
+ **Correct (Pressable from gesture handler for lists):**
42
+
43
+ ```tsx
44
+ import { Pressable } from 'react-native-gesture-handler'
45
+
46
+ function ListItem({ onPress }: { onPress: () => void }) {
47
+ return (
48
+ <Pressable onPress={onPress}>
49
+ <Text>Item</Text>
50
+ </Pressable>
51
+ )
52
+ }
53
+ ```
54
+
55
+ Use `react-native-gesture-handler` Pressable inside scrollable lists for better
56
+ gesture coordination, as long as you are using the ScrollView from
57
+ `react-native-gesture-handler` as well.
58
+
59
+ **For animated press states (scale, opacity changes):** Use `GestureDetector`
60
+ with Reanimated shared values instead of Pressable's style callback. See the
61
+ `animation-gesture-detector-press` rule.
@@ -0,0 +1,65 @@
1
+ ---
2
+ title: Use contentInsetAdjustmentBehavior for Safe Areas
3
+ impact: MEDIUM
4
+ impactDescription: native safe area handling, no layout shifts
5
+ tags: safe-area, scrollview, layout
6
+ ---
7
+
8
+ ## Use contentInsetAdjustmentBehavior for Safe Areas
9
+
10
+ Use `contentInsetAdjustmentBehavior="automatic"` on the root ScrollView instead of wrapping content in SafeAreaView or manual padding. This lets iOS handle safe area insets natively with proper scroll behavior.
11
+
12
+ **Incorrect (SafeAreaView wrapper):**
13
+
14
+ ```tsx
15
+ import { SafeAreaView, ScrollView, View, Text } from 'react-native'
16
+
17
+ function MyScreen() {
18
+ return (
19
+ <SafeAreaView style={{ flex: 1 }}>
20
+ <ScrollView>
21
+ <View>
22
+ <Text>Content</Text>
23
+ </View>
24
+ </ScrollView>
25
+ </SafeAreaView>
26
+ )
27
+ }
28
+ ```
29
+
30
+ **Incorrect (manual safe area padding):**
31
+
32
+ ```tsx
33
+ import { ScrollView, View, Text } from 'react-native'
34
+ import { useSafeAreaInsets } from 'react-native-safe-area-context'
35
+
36
+ function MyScreen() {
37
+ const insets = useSafeAreaInsets()
38
+
39
+ return (
40
+ <ScrollView contentContainerStyle={{ paddingTop: insets.top }}>
41
+ <View>
42
+ <Text>Content</Text>
43
+ </View>
44
+ </ScrollView>
45
+ )
46
+ }
47
+ ```
48
+
49
+ **Correct (native content inset adjustment):**
50
+
51
+ ```tsx
52
+ import { ScrollView, View, Text } from 'react-native'
53
+
54
+ function MyScreen() {
55
+ return (
56
+ <ScrollView contentInsetAdjustmentBehavior='automatic'>
57
+ <View>
58
+ <Text>Content</Text>
59
+ </View>
60
+ </ScrollView>
61
+ )
62
+ }
63
+ ```
64
+
65
+ The native approach handles dynamic safe areas (keyboard, toolbars) and allows content to scroll behind the status bar naturally.