@brookmind/ai-toolkit 1.0.1 → 1.1.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 (155) hide show
  1. package/README.md +54 -14
  2. package/agents/code-reviewer.md +6 -1
  3. package/agents/code-simplifier.md +52 -0
  4. package/bin/cli.js +1 -5
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +321 -0
  8. package/dist/index.js.map +1 -0
  9. package/mcps/context7/.mcp.json +13 -0
  10. package/mcps/expo-mcp/.mcp.json +13 -0
  11. package/mcps/figma-mcp/.mcp.json +4 -6
  12. package/package.json +22 -11
  13. package/skills/pdf-processing-pro/FORMS.md +610 -0
  14. package/skills/pdf-processing-pro/OCR.md +137 -0
  15. package/skills/pdf-processing-pro/SKILL.md +296 -0
  16. package/skills/pdf-processing-pro/TABLES.md +626 -0
  17. package/skills/pdf-processing-pro/scripts/analyze_form.py +307 -0
  18. package/skills/react-best-practices/AGENTS.md +915 -0
  19. package/skills/react-best-practices/README.md +127 -0
  20. package/skills/react-best-practices/SKILL.md +110 -0
  21. package/skills/react-best-practices/metadata.json +14 -0
  22. package/skills/react-best-practices/rules/_sections.md +41 -0
  23. package/skills/react-best-practices/rules/_template.md +28 -0
  24. package/skills/react-best-practices/rules/advanced-event-handler-refs.md +80 -0
  25. package/skills/react-best-practices/rules/advanced-use-latest.md +76 -0
  26. package/skills/react-best-practices/rules/async-defer-await.md +80 -0
  27. package/skills/react-best-practices/rules/async-dependencies.md +36 -0
  28. package/skills/react-best-practices/rules/async-parallel.md +28 -0
  29. package/skills/react-best-practices/rules/async-suspense-boundaries.md +100 -0
  30. package/skills/react-best-practices/rules/bundle-barrel-imports.md +42 -0
  31. package/skills/react-best-practices/rules/bundle-conditional.md +106 -0
  32. package/skills/react-best-practices/rules/bundle-preload.md +44 -0
  33. package/skills/react-best-practices/rules/client-event-listeners.md +131 -0
  34. package/skills/react-best-practices/rules/client-swr-dedup.md +133 -0
  35. package/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  36. package/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  37. package/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  38. package/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  39. package/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  40. package/skills/react-best-practices/rules/js-early-exit.md +50 -0
  41. package/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  42. package/skills/react-best-practices/rules/js-index-maps.md +37 -0
  43. package/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  44. package/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  45. package/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  46. package/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  47. package/skills/react-best-practices/rules/rendering-activity.md +90 -0
  48. package/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  49. package/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  50. package/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  51. package/skills/react-best-practices/rules/rendering-hoist-jsx.md +65 -0
  52. package/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  53. package/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  54. package/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  55. package/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  56. package/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  57. package/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  58. package/skills/react-best-practices/rules/rerender-memo.md +85 -0
  59. package/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  60. package/themes/README.md +68 -0
  61. package/themes/claude-vivid.json +72 -0
  62. package/mcps/context7/.claude-plugin +0 -1
  63. package/mcps/context7/README.md +0 -1
  64. package/mcps/context7/server.json +0 -1
  65. package/mcps/expo-mcp/README.md +0 -33
  66. package/mcps/expo-mcp/package.json +0 -30
  67. package/mcps/figma-mcp/README.md +0 -554
  68. package/mcps/figma-mcp/server.json +0 -17
  69. package/mcps/figma-mcp/skills/code-connect-components +0 -1
  70. package/mcps/figma-mcp/skills/create-design-system-rules +0 -1
  71. package/mcps/figma-mcp/skills/implement-design +0 -1
  72. package/mcps/pg-aiguide/.claude-plugin +0 -1
  73. package/mcps/pg-aiguide/CLAUDE.md +0 -21
  74. package/mcps/pg-aiguide/README.md +0 -275
  75. package/mcps/pg-aiguide/skills/design-postgres-tables +0 -1
  76. package/mcps/pg-aiguide/skills/find-hypertable-candidates +0 -1
  77. package/mcps/pg-aiguide/skills/migrate-postgres-tables-to-hypertables +0 -1
  78. package/mcps/pg-aiguide/skills/setup-timescaledb-hypertables +0 -1
  79. package/mcps/pg-aiguide/skills.yaml +0 -4
  80. package/skills/cloudflare-cli/SKILL.md +0 -151
  81. package/skills/docx/LICENSE.txt +0 -30
  82. package/skills/docx/SKILL.md +0 -197
  83. package/skills/docx/docx-js.md +0 -350
  84. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +0 -1499
  85. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +0 -146
  86. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +0 -1085
  87. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +0 -11
  88. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +0 -3081
  89. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +0 -23
  90. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +0 -185
  91. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +0 -287
  92. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +0 -1676
  93. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +0 -28
  94. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +0 -144
  95. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +0 -174
  96. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +0 -25
  97. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +0 -18
  98. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +0 -59
  99. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +0 -56
  100. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +0 -195
  101. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +0 -582
  102. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +0 -25
  103. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +0 -4439
  104. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +0 -570
  105. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +0 -509
  106. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +0 -12
  107. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +0 -108
  108. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +0 -96
  109. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +0 -3646
  110. package/skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +0 -116
  111. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +0 -42
  112. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +0 -50
  113. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +0 -49
  114. package/skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +0 -33
  115. package/skills/docx/ooxml/schemas/mce/mc.xsd +0 -75
  116. package/skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +0 -560
  117. package/skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +0 -67
  118. package/skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +0 -14
  119. package/skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +0 -20
  120. package/skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +0 -13
  121. package/skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +0 -4
  122. package/skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +0 -8
  123. package/skills/docx/ooxml/scripts/pack.py +0 -159
  124. package/skills/docx/ooxml/scripts/unpack.py +0 -29
  125. package/skills/docx/ooxml/scripts/validate.py +0 -69
  126. package/skills/docx/ooxml/scripts/validation/__init__.py +0 -15
  127. package/skills/docx/ooxml/scripts/validation/base.py +0 -951
  128. package/skills/docx/ooxml/scripts/validation/docx.py +0 -274
  129. package/skills/docx/ooxml/scripts/validation/pptx.py +0 -315
  130. package/skills/docx/ooxml/scripts/validation/redlining.py +0 -279
  131. package/skills/docx/ooxml.md +0 -610
  132. package/skills/docx/scripts/__init__.py +0 -1
  133. package/skills/docx/scripts/document.py +0 -1276
  134. package/skills/docx/scripts/templates/comments.xml +0 -3
  135. package/skills/docx/scripts/templates/commentsExtended.xml +0 -3
  136. package/skills/docx/scripts/templates/commentsExtensible.xml +0 -3
  137. package/skills/docx/scripts/templates/commentsIds.xml +0 -3
  138. package/skills/docx/scripts/templates/people.xml +0 -3
  139. package/skills/docx/scripts/utilities.py +0 -374
  140. package/skills/pdf/LICENSE.txt +0 -30
  141. package/skills/pdf/SKILL.md +0 -294
  142. package/skills/pdf/forms.md +0 -205
  143. package/skills/pdf/reference.md +0 -612
  144. package/skills/pdf/scripts/check_bounding_boxes.py +0 -70
  145. package/skills/pdf/scripts/check_bounding_boxes_test.py +0 -226
  146. package/skills/pdf/scripts/check_fillable_fields.py +0 -12
  147. package/skills/pdf/scripts/convert_pdf_to_images.py +0 -35
  148. package/skills/pdf/scripts/create_validation_image.py +0 -41
  149. package/skills/pdf/scripts/extract_form_field_info.py +0 -152
  150. package/skills/pdf/scripts/fill_fillable_fields.py +0 -114
  151. package/skills/pdf/scripts/fill_pdf_form_with_annotations.py +0 -108
  152. package/skills/xlsx/LICENSE.txt +0 -30
  153. package/skills/xlsx/SKILL.md +0 -289
  154. package/skills/xlsx/recalc.py +0 -178
  155. package/src/index.js +0 -365
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: Use Explicit Conditional Rendering
3
+ impact: LOW
4
+ impactDescription: prevents rendering 0 or NaN
5
+ tags: rendering, conditional, jsx, falsy-values
6
+ ---
7
+
8
+ ## Use Explicit Conditional Rendering
9
+
10
+ Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.
11
+
12
+ **Incorrect (renders "0" when count is 0):**
13
+
14
+ ```tsx
15
+ function Badge({ count }: { count: number }) {
16
+ return (
17
+ <div>
18
+ {count && <span className="badge">{count}</span>}
19
+ </div>
20
+ )
21
+ }
22
+
23
+ // When count = 0, renders: <div>0</div>
24
+ // When count = 5, renders: <div><span class="badge">5</span></div>
25
+ ```
26
+
27
+ **Correct (renders nothing when count is 0):**
28
+
29
+ ```tsx
30
+ function Badge({ count }: { count: number }) {
31
+ return (
32
+ <div>
33
+ {count > 0 ? <span className="badge">{count}</span> : null}
34
+ </div>
35
+ )
36
+ }
37
+
38
+ // When count = 0, renders: <div></div>
39
+ // When count = 5, renders: <div><span class="badge">5</span></div>
40
+ ```
@@ -0,0 +1,38 @@
1
+ ---
2
+ title: CSS content-visibility for Long Lists
3
+ impact: HIGH
4
+ impactDescription: faster initial render
5
+ tags: rendering, css, content-visibility, long-lists
6
+ ---
7
+
8
+ ## CSS content-visibility for Long Lists
9
+
10
+ Apply `content-visibility: auto` to defer off-screen rendering.
11
+
12
+ **CSS:**
13
+
14
+ ```css
15
+ .message-item {
16
+ content-visibility: auto;
17
+ contain-intrinsic-size: 0 80px;
18
+ }
19
+ ```
20
+
21
+ **Example:**
22
+
23
+ ```tsx
24
+ function MessageList({ messages }: { messages: Message[] }) {
25
+ return (
26
+ <div className="overflow-y-auto h-screen">
27
+ {messages.map(msg => (
28
+ <div key={msg.id} className="message-item">
29
+ <Avatar user={msg.author} />
30
+ <div>{msg.content}</div>
31
+ </div>
32
+ ))}
33
+ </div>
34
+ )
35
+ }
36
+ ```
37
+
38
+ For 1000 messages, browser skips layout/paint for ~990 off-screen items (10× faster initial render).
@@ -0,0 +1,65 @@
1
+ ---
2
+ title: Hoist Static JSX Elements
3
+ impact: LOW
4
+ impactDescription: avoids re-creation
5
+ tags: rendering, jsx, static, react-compiler, optimization
6
+ ---
7
+
8
+ ## Hoist Static JSX Elements
9
+
10
+ With React 19 Compiler, static JSX is automatically hoisted. Without the Compiler, manually extract static JSX outside components to avoid re-creation.
11
+
12
+ **With React 19 Compiler (Recommended):**
13
+
14
+ The React Compiler automatically detects and hoists static JSX. Just write normal components:
15
+
16
+ ```tsx
17
+ function LoadingSkeleton() {
18
+ return <div className="animate-pulse h-20 bg-gray-200" />;
19
+ }
20
+
21
+ function Container({ loading }: { loading: boolean }) {
22
+ return <div>{loading && <LoadingSkeleton />}</div>;
23
+ }
24
+ // Compiler automatically optimizes this!
25
+ ```
26
+
27
+ **Without React Compiler (Manual Optimization):**
28
+
29
+ If you're not using the Compiler, manually hoist static elements:
30
+
31
+ **Before (recreates element every render):**
32
+
33
+ ```tsx
34
+ function Container({ loading }: { loading: boolean }) {
35
+ // This JSX is recreated on every render
36
+ const skeleton = <div className="animate-pulse h-20 bg-gray-200" />;
37
+
38
+ return <div>{loading && skeleton}</div>;
39
+ }
40
+ ```
41
+
42
+ **After (reuses same element):**
43
+
44
+ ```tsx
45
+ // Hoisted to module scope - created once
46
+ const loadingSkeleton = <div className="animate-pulse h-20 bg-gray-200" />;
47
+
48
+ function Container({ loading }: { loading: boolean }) {
49
+ return <div>{loading && loadingSkeleton}</div>;
50
+ }
51
+ ```
52
+
53
+ **Best use cases for manual hoisting:**
54
+
55
+ - Large static SVG icons (expensive to recreate)
56
+ - Complex static layouts that never change
57
+ - Decorative elements with no dynamic props
58
+
59
+ **When NOT to hoist:**
60
+
61
+ - Elements with dynamic props or children
62
+ - Elements that need access to component scope (hooks, state)
63
+ - Small simple elements (overhead not worth it)
64
+
65
+ Reference: [React Compiler](https://react.dev/learn/react-compiler)
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Optimize SVG Precision
3
+ impact: LOW
4
+ impactDescription: reduces file size
5
+ tags: rendering, svg, optimization, svgo
6
+ ---
7
+
8
+ ## Optimize SVG Precision
9
+
10
+ Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.
11
+
12
+ **Incorrect (excessive precision):**
13
+
14
+ ```svg
15
+ <path d="M 10.293847 20.847362 L 30.938472 40.192837" />
16
+ ```
17
+
18
+ **Correct (1 decimal place):**
19
+
20
+ ```svg
21
+ <path d="M 10.3 20.8 L 30.9 40.2" />
22
+ ```
23
+
24
+ **Automate with SVGO:**
25
+
26
+ ```bash
27
+ npx svgo --precision=1 --multipass icon.svg
28
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ title: Defer State Reads to Usage Point
3
+ impact: MEDIUM
4
+ impactDescription: avoids unnecessary subscriptions
5
+ tags: rerender, searchParams, localStorage, optimization
6
+ ---
7
+
8
+ ## Defer State Reads to Usage Point
9
+
10
+ Don't subscribe to dynamic state (searchParams, localStorage) if you only read it inside callbacks.
11
+
12
+ **Incorrect (subscribes to all searchParams changes):**
13
+
14
+ ```tsx
15
+ function ShareButton({ chatId }: { chatId: string }) {
16
+ const searchParams = useSearchParams()
17
+
18
+ const handleShare = () => {
19
+ const ref = searchParams.get('ref')
20
+ shareChat(chatId, { ref })
21
+ }
22
+
23
+ return <button onClick={handleShare}>Share</button>
24
+ }
25
+ ```
26
+
27
+ **Correct (reads on demand, no subscription):**
28
+
29
+ ```tsx
30
+ function ShareButton({ chatId }: { chatId: string }) {
31
+ const handleShare = () => {
32
+ const params = new URLSearchParams(window.location.search)
33
+ const ref = params.get('ref')
34
+ shareChat(chatId, { ref })
35
+ }
36
+
37
+ return <button onClick={handleShare}>Share</button>
38
+ }
39
+ ```
@@ -0,0 +1,45 @@
1
+ ---
2
+ title: Narrow Effect Dependencies
3
+ impact: LOW
4
+ impactDescription: minimizes effect re-runs
5
+ tags: rerender, useEffect, dependencies, optimization
6
+ ---
7
+
8
+ ## Narrow Effect Dependencies
9
+
10
+ Specify primitive dependencies instead of objects to minimize effect re-runs.
11
+
12
+ **Incorrect (re-runs on any user field change):**
13
+
14
+ ```tsx
15
+ useEffect(() => {
16
+ console.log(user.id)
17
+ }, [user])
18
+ ```
19
+
20
+ **Correct (re-runs only when id changes):**
21
+
22
+ ```tsx
23
+ useEffect(() => {
24
+ console.log(user.id)
25
+ }, [user.id])
26
+ ```
27
+
28
+ **For derived state, compute outside effect:**
29
+
30
+ ```tsx
31
+ // Incorrect: runs on width=767, 766, 765...
32
+ useEffect(() => {
33
+ if (width < 768) {
34
+ enableMobileMode()
35
+ }
36
+ }, [width])
37
+
38
+ // Correct: runs only on boolean transition
39
+ const isMobile = width < 768
40
+ useEffect(() => {
41
+ if (isMobile) {
42
+ enableMobileMode()
43
+ }
44
+ }, [isMobile])
45
+ ```
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Subscribe to Derived State
3
+ impact: MEDIUM
4
+ impactDescription: reduces re-render frequency
5
+ tags: rerender, derived-state, media-query, optimization
6
+ ---
7
+
8
+ ## Subscribe to Derived State
9
+
10
+ Subscribe to derived boolean state instead of continuous values to reduce re-render frequency.
11
+
12
+ **Incorrect (re-renders on every pixel change):**
13
+
14
+ ```tsx
15
+ function Sidebar() {
16
+ const width = useWindowWidth() // updates continuously
17
+ const isMobile = width < 768
18
+ return <nav className={isMobile ? 'mobile' : 'desktop'}>
19
+ }
20
+ ```
21
+
22
+ **Correct (re-renders only when boolean changes):**
23
+
24
+ ```tsx
25
+ function Sidebar() {
26
+ const isMobile = useMediaQuery('(max-width: 767px)')
27
+ return <nav className={isMobile ? 'mobile' : 'desktop'}>
28
+ }
29
+ ```
@@ -0,0 +1,74 @@
1
+ ---
2
+ title: Use Functional setState Updates
3
+ impact: MEDIUM
4
+ impactDescription: prevents stale closures and unnecessary callback recreations
5
+ tags: react, hooks, useState, useCallback, callbacks, closures
6
+ ---
7
+
8
+ ## Use Functional setState Updates
9
+
10
+ When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.
11
+
12
+ **Incorrect (requires state as dependency):**
13
+
14
+ ```tsx
15
+ function TodoList() {
16
+ const [items, setItems] = useState(initialItems)
17
+
18
+ // Callback must depend on items, recreated on every items change
19
+ const addItems = useCallback((newItems: Item[]) => {
20
+ setItems([...items, ...newItems])
21
+ }, [items]) // ❌ items dependency causes recreations
22
+
23
+ // Risk of stale closure if dependency is forgotten
24
+ const removeItem = useCallback((id: string) => {
25
+ setItems(items.filter(item => item.id !== id))
26
+ }, []) // ❌ Missing items dependency - will use stale items!
27
+
28
+ return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
29
+ }
30
+ ```
31
+
32
+ The first callback is recreated every time `items` changes, which can cause child components to re-render unnecessarily. The second callback has a stale closure bug—it will always reference the initial `items` value.
33
+
34
+ **Correct (stable callbacks, no stale closures):**
35
+
36
+ ```tsx
37
+ function TodoList() {
38
+ const [items, setItems] = useState(initialItems)
39
+
40
+ // Stable callback, never recreated
41
+ const addItems = useCallback((newItems: Item[]) => {
42
+ setItems(curr => [...curr, ...newItems])
43
+ }, []) // ✅ No dependencies needed
44
+
45
+ // Always uses latest state, no stale closure risk
46
+ const removeItem = useCallback((id: string) => {
47
+ setItems(curr => curr.filter(item => item.id !== id))
48
+ }, []) // ✅ Safe and stable
49
+
50
+ return <ItemsEditor items={items} onAdd={addItems} onRemove={removeItem} />
51
+ }
52
+ ```
53
+
54
+ **Benefits:**
55
+
56
+ 1. **Stable callback references** - Callbacks don't need to be recreated when state changes
57
+ 2. **No stale closures** - Always operates on the latest state value
58
+ 3. **Fewer dependencies** - Simplifies dependency arrays and reduces memory leaks
59
+ 4. **Prevents bugs** - Eliminates the most common source of React closure bugs
60
+
61
+ **When to use functional updates:**
62
+
63
+ - Any setState that depends on the current state value
64
+ - Inside useCallback/useMemo when state is needed
65
+ - Event handlers that reference state
66
+ - Async operations that update state
67
+
68
+ **When direct updates are fine:**
69
+
70
+ - Setting state to a static value: `setCount(0)`
71
+ - Setting state from props/arguments only: `setName(newName)`
72
+ - State doesn't depend on previous value
73
+
74
+ **Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler can automatically optimize some cases, but functional updates are still recommended for correctness and to prevent stale closure bugs.
@@ -0,0 +1,58 @@
1
+ ---
2
+ title: Use Lazy State Initialization
3
+ impact: MEDIUM
4
+ impactDescription: wasted computation on every render
5
+ tags: react, hooks, useState, performance, initialization
6
+ ---
7
+
8
+ ## Use Lazy State Initialization
9
+
10
+ Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.
11
+
12
+ **Incorrect (runs on every render):**
13
+
14
+ ```tsx
15
+ function FilteredList({ items }: { items: Item[] }) {
16
+ // buildSearchIndex() runs on EVERY render, even after initialization
17
+ const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items))
18
+ const [query, setQuery] = useState('')
19
+
20
+ // When query changes, buildSearchIndex runs again unnecessarily
21
+ return <SearchResults index={searchIndex} query={query} />
22
+ }
23
+
24
+ function UserProfile() {
25
+ // JSON.parse runs on every render
26
+ const [settings, setSettings] = useState(
27
+ JSON.parse(localStorage.getItem('settings') || '{}')
28
+ )
29
+
30
+ return <SettingsForm settings={settings} onChange={setSettings} />
31
+ }
32
+ ```
33
+
34
+ **Correct (runs only once):**
35
+
36
+ ```tsx
37
+ function FilteredList({ items }: { items: Item[] }) {
38
+ // buildSearchIndex() runs ONLY on initial render
39
+ const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items))
40
+ const [query, setQuery] = useState('')
41
+
42
+ return <SearchResults index={searchIndex} query={query} />
43
+ }
44
+
45
+ function UserProfile() {
46
+ // JSON.parse runs only on initial render
47
+ const [settings, setSettings] = useState(() => {
48
+ const stored = localStorage.getItem('settings')
49
+ return stored ? JSON.parse(stored) : {}
50
+ })
51
+
52
+ return <SettingsForm settings={settings} onChange={setSettings} />
53
+ }
54
+ ```
55
+
56
+ Use lazy initialization when computing initial values from localStorage/sessionStorage, building data structures (indexes, maps), reading from the DOM, or performing heavy transformations.
57
+
58
+ For simple primitives (`useState(0)`), direct references (`useState(props.value)`), or cheap literals (`useState({})`), the function form is unnecessary.
@@ -0,0 +1,85 @@
1
+ ---
2
+ title: Extract to Memoized Components
3
+ impact: MEDIUM
4
+ impactDescription: enables early returns and skips computation
5
+ tags: rerender, memo, useMemo, react-compiler, optimization
6
+ ---
7
+
8
+ ## Extract to Memoized Components
9
+
10
+ Extract expensive work into separate components to enable early returns before computation. With React 19 Compiler, this happens automatically.
11
+
12
+ **With React 19 Compiler (Recommended):**
13
+
14
+ If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, you don't need manual memoization. The compiler automatically:
15
+
16
+ - Memoizes component renders
17
+ - Caches expensive computations
18
+ - Optimizes re-renders
19
+
20
+ Just write clean code without `memo()` or `useMemo()`:
21
+
22
+ ```tsx
23
+ function UserAvatar({ user }: { user: User }) {
24
+ const id = computeAvatarId(user); // Compiler auto-memoizes
25
+ return <Avatar id={id} />;
26
+ }
27
+
28
+ function Profile({ user, loading }: Props) {
29
+ if (loading) return <Skeleton />;
30
+ return (
31
+ <div>
32
+ <UserAvatar user={user} /> {/* Compiler auto-memoizes */}
33
+ </div>
34
+ );
35
+ }
36
+ ```
37
+
38
+ **Without React Compiler (Manual Optimization):**
39
+
40
+ If you're not using the React Compiler, use `memo()` and `useMemo()` manually:
41
+
42
+ **Incorrect (computes avatar even when loading):**
43
+
44
+ ```tsx
45
+ function Profile({ user, loading }: Props) {
46
+ // This runs on every render, even when loading=true
47
+ const avatar = useMemo(() => {
48
+ const id = computeAvatarId(user);
49
+ return <Avatar id={id} />;
50
+ }, [user]);
51
+
52
+ if (loading) return <Skeleton />;
53
+ return <div>{avatar}</div>;
54
+ }
55
+ ```
56
+
57
+ **Correct (skips computation when loading):**
58
+
59
+ ```tsx
60
+ const UserAvatar = memo(function UserAvatar({ user }: { user: User }) {
61
+ const id = useMemo(() => computeAvatarId(user), [user]);
62
+ return <Avatar id={id} />;
63
+ });
64
+
65
+ function Profile({ user, loading }: Props) {
66
+ if (loading) return <Skeleton />;
67
+ return (
68
+ <div>
69
+ <UserAvatar user={user} />
70
+ </div>
71
+ );
72
+ }
73
+ ```
74
+
75
+ **Key insight:** Early returns before component JSX skip that component's computation entirely. Extract expensive work into child components that appear after conditionals.
76
+
77
+ **When to still use memo() with Compiler:**
78
+
79
+ Even with the Compiler, `memo()` can be useful for:
80
+
81
+ - Components receiving frequently-changing callback props
82
+ - Components in lists with stable item identity
83
+ - Performance-critical components where you want explicit control
84
+
85
+ Reference: [React Compiler](https://react.dev/learn/react-compiler)
@@ -0,0 +1,40 @@
1
+ ---
2
+ title: Use Transitions for Non-Urgent Updates
3
+ impact: MEDIUM
4
+ impactDescription: maintains UI responsiveness
5
+ tags: rerender, transitions, startTransition, performance
6
+ ---
7
+
8
+ ## Use Transitions for Non-Urgent Updates
9
+
10
+ Mark frequent, non-urgent state updates as transitions to maintain UI responsiveness.
11
+
12
+ **Incorrect (blocks UI on every scroll):**
13
+
14
+ ```tsx
15
+ function ScrollTracker() {
16
+ const [scrollY, setScrollY] = useState(0)
17
+ useEffect(() => {
18
+ const handler = () => setScrollY(window.scrollY)
19
+ window.addEventListener('scroll', handler, { passive: true })
20
+ return () => window.removeEventListener('scroll', handler)
21
+ }, [])
22
+ }
23
+ ```
24
+
25
+ **Correct (non-blocking updates):**
26
+
27
+ ```tsx
28
+ import { startTransition } from 'react'
29
+
30
+ function ScrollTracker() {
31
+ const [scrollY, setScrollY] = useState(0)
32
+ useEffect(() => {
33
+ const handler = () => {
34
+ startTransition(() => setScrollY(window.scrollY))
35
+ }
36
+ window.addEventListener('scroll', handler, { passive: true })
37
+ return () => window.removeEventListener('scroll', handler)
38
+ }, [])
39
+ }
40
+ ```
@@ -0,0 +1,68 @@
1
+ # Claude Vivid Theme
2
+
3
+ A vibrant, high-contrast theme for OpenCode inspired by Claude Code's aesthetic.
4
+
5
+ ## Features
6
+
7
+ - **Pure black background** (#000000) for maximum contrast
8
+ - **Vivid syntax highlighting** with bright, saturated colors
9
+ - **Neutral menu interactions** - Gray selection highlights instead of orange
10
+ - **Optimized for dark mode** terminal usage
11
+
12
+ ## Color Palette
13
+
14
+ ### Primary Colors
15
+ - Orange: `#FF6B35` - Headings, variables
16
+ - Purple: `#A855F7` - Keywords
17
+ - Cyan: `#22D3EE` - Functions, links
18
+ - Green: `#4ADE80` - Strings, success states
19
+ - Yellow: `#FACC15` - Types, warnings
20
+ - Pink: `#EC4899` - Numbers
21
+ - Red: `#EF4444` - Errors
22
+
23
+ ### UI Colors
24
+ - Background: `#000000` (pure black)
25
+ - Panel: `#0a0a0a` (almost black)
26
+ - Elements: `#1a1a1a` (very dark gray)
27
+ - Borders: `#2a2a2a` (dark gray)
28
+ - Selection: `#E5E5E5` (light gray - neutral)
29
+
30
+ ## Installation
31
+
32
+ This theme is automatically installed when you run the AI Toolkit installer. It will be copied to `~/.config/opencode/themes/claude-vivid.json` and configured in your `opencode.json`.
33
+
34
+ ## Manual Installation
35
+
36
+ If you want to install it manually:
37
+
38
+ 1. Create the themes directory:
39
+ ```bash
40
+ mkdir -p ~/.config/opencode/themes
41
+ ```
42
+
43
+ 2. Copy the theme file:
44
+ ```bash
45
+ cp themes/claude-vivid.json ~/.config/opencode/themes/
46
+ ```
47
+
48
+ 3. Update your `~/.config/opencode/opencode.json`:
49
+ ```json
50
+ {
51
+ "$schema": "https://opencode.ai/config.json",
52
+ "theme": "claude-vivid"
53
+ }
54
+ ```
55
+
56
+ 4. Restart OpenCode or run `/theme claude-vivid`
57
+
58
+ ## Customization
59
+
60
+ You can modify the theme by editing `~/.config/opencode/themes/claude-vivid.json`. All colors use the OpenCode theme schema with support for:
61
+
62
+ - Hex colors: `"#ffffff"`
63
+ - Color references: `"orange"` (defined in `defs`)
64
+ - Dark/light variants: `{"dark": "#000", "light": "#fff"}`
65
+
66
+ ## Credits
67
+
68
+ Created for the OpenCode AI Toolkit by @davidcastillog