@pageai/ralph-loop 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agent/PROMPT.md +58 -0
- package/.agent/STEERING.md +3 -0
- package/.agent/logs/LOG.md +13 -0
- package/.agent/prd/.gitkeep +0 -0
- package/.agent/screenshots/.gitkeep +0 -0
- package/.agent/skills/component-refactoring/SKILL.md +247 -0
- package/.agent/skills/component-refactoring/references/complexity-patterns.md +485 -0
- package/.agent/skills/component-refactoring/references/component-splitting.md +419 -0
- package/.agent/skills/component-refactoring/references/hook-extraction.md +317 -0
- package/.agent/skills/e2e-tester/SKILL.md +595 -0
- package/.agent/skills/frontend-code-review/SKILL.md +73 -0
- package/.agent/skills/frontend-code-review/references/code-quality.md +28 -0
- package/.agent/skills/frontend-code-review/references/performance.md +36 -0
- package/.agent/skills/frontend-testing/SKILL.md +316 -0
- package/.agent/skills/frontend-testing/assets/component-test.template.tsx +293 -0
- package/.agent/skills/frontend-testing/assets/hook-test.template.ts +207 -0
- package/.agent/skills/frontend-testing/assets/utility-test.template.ts +154 -0
- package/.agent/skills/frontend-testing/references/async-testing.md +345 -0
- package/.agent/skills/frontend-testing/references/checklist.md +188 -0
- package/.agent/skills/frontend-testing/references/common-patterns.md +449 -0
- package/.agent/skills/frontend-testing/references/mocking.md +289 -0
- package/.agent/skills/frontend-testing/references/workflow.md +265 -0
- package/.agent/skills/prd-creator/JSON.md +613 -0
- package/.agent/skills/prd-creator/PRD.md +196 -0
- package/.agent/skills/prd-creator/SKILL.md +143 -0
- package/.agent/skills/skill-creator/SKILL.md +355 -0
- package/.agent/skills/skill-creator/references/output-patterns.md +86 -0
- package/.agent/skills/skill-creator/references/workflows.md +28 -0
- package/.agent/skills/skill-creator/scripts/init_skill.py +300 -0
- package/.agent/skills/skill-creator/scripts/package_skill.py +110 -0
- package/.agent/skills/vercel-react-best-practices/AGENTS.md +2249 -0
- package/.agent/skills/vercel-react-best-practices/SKILL.md +125 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/.agent/skills/vercel-react-best-practices/rules/advanced-use-latest.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-api-routes.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-defer-await.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-dependencies.md +36 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-parallel.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-conditional.md +31 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/.agent/skills/vercel-react-best-practices/rules/bundle-preload.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-event-listeners.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/client-swr-dedup.md +56 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-function-results.md +80 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-property-access.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-cache-storage.md +70 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-combine-iterations.md +32 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-early-exit.md +50 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-index-maps.md +37 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-length-check-first.md +49 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-min-max-loop.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/.agent/skills/vercel-react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-activity.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/.agent/skills/vercel-react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-dependencies.md +45 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-derived-state.md +29 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-memo.md +44 -0
- package/.agent/skills/vercel-react-best-practices/rules/rerender-transitions.md +40 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-lru.md +41 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-cache-react.md +26 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/.agent/skills/vercel-react-best-practices/rules/server-serialization.md +38 -0
- package/.agent/skills/vitest-best-practices/AGENTS.md +84 -0
- package/.agent/skills/vitest-best-practices/SKILL.md +130 -0
- package/.agent/skills/vitest-best-practices/references/aaa-pattern.md +260 -0
- package/.agent/skills/vitest-best-practices/references/assertions.md +393 -0
- package/.agent/skills/vitest-best-practices/references/async-testing.md +454 -0
- package/.agent/skills/vitest-best-practices/references/error-handling.md +382 -0
- package/.agent/skills/vitest-best-practices/references/organization.md +212 -0
- package/.agent/skills/vitest-best-practices/references/parameterized-tests.md +297 -0
- package/.agent/skills/vitest-best-practices/references/performance.md +528 -0
- package/.agent/skills/vitest-best-practices/references/snapshot-testing.md +483 -0
- package/.agent/skills/vitest-best-practices/references/test-doubles.md +499 -0
- package/.agent/skills/vitest-best-practices/references/vitest-features.md +529 -0
- package/.agent/skills/web-design-guidelines/SKILL.md +39 -0
- package/.agent/tasks/.gitkeep +0 -0
- package/.agent/tasks.json +1 -0
- package/.claude/agents/code-reviewer.md +172 -0
- package/.claude/commands/aw.md +50 -0
- package/.claude/hooks/play-sound.js +87 -0
- package/.claude/hooks/pre-tool-use.js +40 -0
- package/.claude/settings.json +54 -0
- package/.claude/settings.local.json +13 -0
- package/.mcp.json +31 -0
- package/AGENTS.md +44 -0
- package/CLAUDE.md +1 -0
- package/README.md +236 -0
- package/bin/cli.js +156 -0
- package/bin/lib/copy.js +149 -0
- package/bin/lib/display.js +137 -0
- package/package.json +65 -0
- package/ralph.sh +333 -0
- package/scripts/lib/args.sh +44 -0
- package/scripts/lib/cleanup.sh +53 -0
- package/scripts/lib/constants.sh +25 -0
- package/scripts/lib/display.sh +196 -0
- package/scripts/lib/logging.sh +30 -0
- package/scripts/lib/notify.sh +41 -0
- package/scripts/lib/output.sh +147 -0
- package/scripts/lib/preflight.sh +57 -0
- package/scripts/lib/preview.sh +77 -0
- package/scripts/lib/promise.sh +76 -0
- package/scripts/lib/spinner.sh +85 -0
- package/scripts/lib/terminal.sh +57 -0
- package/scripts/lib/timing.sh +223 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# Complexity Reduction Patterns
|
|
2
|
+
|
|
3
|
+
This document provides patterns for reducing cognitive complexity in React components.
|
|
4
|
+
|
|
5
|
+
## Understanding Complexity
|
|
6
|
+
|
|
7
|
+
### What Increases Complexity
|
|
8
|
+
|
|
9
|
+
| Pattern | Complexity Impact |
|
|
10
|
+
| ------------------- | -------------------- |
|
|
11
|
+
| `if/else` | +1 per branch |
|
|
12
|
+
| Nested conditions | +1 per nesting level |
|
|
13
|
+
| `switch/case` | +1 per case |
|
|
14
|
+
| `for/while/do` | +1 per loop |
|
|
15
|
+
| `&&`/` | | ` chains | +1 per operator |
|
|
16
|
+
| Nested callbacks | +1 per nesting level |
|
|
17
|
+
| `try/catch` | +1 per catch |
|
|
18
|
+
| Ternary expressions | +1 per nesting |
|
|
19
|
+
|
|
20
|
+
## Pattern 1: Replace Conditionals with Lookup Tables
|
|
21
|
+
|
|
22
|
+
**Before** (complexity: ~15):
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
const Template = useMemo(() => {
|
|
26
|
+
if (appDetail?.mode === AppModeEnum.CHAT) {
|
|
27
|
+
switch (locale) {
|
|
28
|
+
case LanguagesSupported[1]:
|
|
29
|
+
return <TemplateChatZh appDetail={appDetail} />
|
|
30
|
+
case LanguagesSupported[7]:
|
|
31
|
+
return <TemplateChatJa appDetail={appDetail} />
|
|
32
|
+
default:
|
|
33
|
+
return <TemplateChatEn appDetail={appDetail} />
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (appDetail?.mode === AppModeEnum.ADVANCED_CHAT) {
|
|
37
|
+
switch (locale) {
|
|
38
|
+
case LanguagesSupported[1]:
|
|
39
|
+
return <TemplateAdvancedChatZh appDetail={appDetail} />
|
|
40
|
+
case LanguagesSupported[7]:
|
|
41
|
+
return <TemplateAdvancedChatJa appDetail={appDetail} />
|
|
42
|
+
default:
|
|
43
|
+
return <TemplateAdvancedChatEn appDetail={appDetail} />
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (appDetail?.mode === AppModeEnum.WORKFLOW) {
|
|
47
|
+
// Similar pattern...
|
|
48
|
+
}
|
|
49
|
+
return null
|
|
50
|
+
}, [appDetail, locale])
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**After** (complexity: ~3):
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Define lookup table outside component
|
|
57
|
+
const TEMPLATE_MAP: Record<AppModeEnum, Record<string, FC<TemplateProps>>> = {
|
|
58
|
+
[AppModeEnum.CHAT]: {
|
|
59
|
+
[LanguagesSupported[1]]: TemplateChatZh,
|
|
60
|
+
[LanguagesSupported[7]]: TemplateChatJa,
|
|
61
|
+
default: TemplateChatEn,
|
|
62
|
+
},
|
|
63
|
+
[AppModeEnum.ADVANCED_CHAT]: {
|
|
64
|
+
[LanguagesSupported[1]]: TemplateAdvancedChatZh,
|
|
65
|
+
[LanguagesSupported[7]]: TemplateAdvancedChatJa,
|
|
66
|
+
default: TemplateAdvancedChatEn,
|
|
67
|
+
},
|
|
68
|
+
[AppModeEnum.WORKFLOW]: {
|
|
69
|
+
[LanguagesSupported[1]]: TemplateWorkflowZh,
|
|
70
|
+
[LanguagesSupported[7]]: TemplateWorkflowJa,
|
|
71
|
+
default: TemplateWorkflowEn,
|
|
72
|
+
},
|
|
73
|
+
// ...
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Clean component logic
|
|
77
|
+
const Template = useMemo(() => {
|
|
78
|
+
if (!appDetail?.mode) return null
|
|
79
|
+
|
|
80
|
+
const templates = TEMPLATE_MAP[appDetail.mode]
|
|
81
|
+
if (!templates) return null
|
|
82
|
+
|
|
83
|
+
const TemplateComponent = templates[locale] ?? templates.default
|
|
84
|
+
return <TemplateComponent appDetail={appDetail} />
|
|
85
|
+
}, [appDetail, locale])
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Pattern 2: Use Early Returns
|
|
89
|
+
|
|
90
|
+
**Before** (complexity: ~10):
|
|
91
|
+
|
|
92
|
+
```typescript
|
|
93
|
+
const handleSubmit = () => {
|
|
94
|
+
if (isValid) {
|
|
95
|
+
if (hasChanges) {
|
|
96
|
+
if (isConnected) {
|
|
97
|
+
submitData()
|
|
98
|
+
} else {
|
|
99
|
+
showConnectionError()
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
showNoChangesMessage()
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
showValidationError()
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
**After** (complexity: ~4):
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
const handleSubmit = () => {
|
|
114
|
+
if (!isValid) {
|
|
115
|
+
showValidationError()
|
|
116
|
+
return
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (!hasChanges) {
|
|
120
|
+
showNoChangesMessage()
|
|
121
|
+
return
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!isConnected) {
|
|
125
|
+
showConnectionError()
|
|
126
|
+
return
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
submitData()
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Pattern 3: Extract Complex Conditions
|
|
134
|
+
|
|
135
|
+
**Before** (complexity: high):
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
const canPublish = (() => {
|
|
139
|
+
if (mode !== AppModeEnum.COMPLETION) {
|
|
140
|
+
if (!isAdvancedMode)
|
|
141
|
+
return true
|
|
142
|
+
|
|
143
|
+
if (modelModeType === ModelModeType.completion) {
|
|
144
|
+
if (!hasSetBlockStatus.history || !hasSetBlockStatus.query)
|
|
145
|
+
return false
|
|
146
|
+
return true
|
|
147
|
+
}
|
|
148
|
+
return true
|
|
149
|
+
}
|
|
150
|
+
return !promptEmpty
|
|
151
|
+
})()
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**After** (complexity: lower):
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Extract to named functions
|
|
158
|
+
const canPublishInCompletionMode = () => !promptEmpty
|
|
159
|
+
|
|
160
|
+
const canPublishInChatMode = () => {
|
|
161
|
+
if (!isAdvancedMode) return true
|
|
162
|
+
if (modelModeType !== ModelModeType.completion) return true
|
|
163
|
+
return hasSetBlockStatus.history && hasSetBlockStatus.query
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Clean main logic
|
|
167
|
+
const canPublish = mode === AppModeEnum.COMPLETION
|
|
168
|
+
? canPublishInCompletionMode()
|
|
169
|
+
: canPublishInChatMode()
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Pattern 4: Replace Chained Ternaries
|
|
173
|
+
|
|
174
|
+
**Before** (complexity: ~5):
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
const statusText = serverActivated
|
|
178
|
+
? t('status.running')
|
|
179
|
+
: serverPublished
|
|
180
|
+
? t('status.inactive')
|
|
181
|
+
: appUnpublished
|
|
182
|
+
? t('status.unpublished')
|
|
183
|
+
: t('status.notConfigured')
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
**After** (complexity: ~2):
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const getStatusText = () => {
|
|
190
|
+
if (serverActivated) return t('status.running')
|
|
191
|
+
if (serverPublished) return t('status.inactive')
|
|
192
|
+
if (appUnpublished) return t('status.unpublished')
|
|
193
|
+
return t('status.notConfigured')
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const statusText = getStatusText()
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Or use lookup:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
const STATUS_TEXT_MAP = {
|
|
203
|
+
running: 'status.running',
|
|
204
|
+
inactive: 'status.inactive',
|
|
205
|
+
unpublished: 'status.unpublished',
|
|
206
|
+
notConfigured: 'status.notConfigured',
|
|
207
|
+
} as const
|
|
208
|
+
|
|
209
|
+
const getStatusKey = (): keyof typeof STATUS_TEXT_MAP => {
|
|
210
|
+
if (serverActivated) return 'running'
|
|
211
|
+
if (serverPublished) return 'inactive'
|
|
212
|
+
if (appUnpublished) return 'unpublished'
|
|
213
|
+
return 'notConfigured'
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const statusText = t(STATUS_TEXT_MAP[getStatusKey()])
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
## Pattern 5: Flatten Nested Loops
|
|
220
|
+
|
|
221
|
+
**Before** (complexity: high):
|
|
222
|
+
|
|
223
|
+
```typescript
|
|
224
|
+
const processData = (items: Item[]) => {
|
|
225
|
+
const results: ProcessedItem[] = []
|
|
226
|
+
|
|
227
|
+
for (const item of items) {
|
|
228
|
+
if (item.isValid) {
|
|
229
|
+
for (const child of item.children) {
|
|
230
|
+
if (child.isActive) {
|
|
231
|
+
for (const prop of child.properties) {
|
|
232
|
+
if (prop.value !== null) {
|
|
233
|
+
results.push({
|
|
234
|
+
itemId: item.id,
|
|
235
|
+
childId: child.id,
|
|
236
|
+
propValue: prop.value,
|
|
237
|
+
})
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
return results
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
**After** (complexity: lower):
|
|
250
|
+
|
|
251
|
+
```typescript
|
|
252
|
+
// Use functional approach
|
|
253
|
+
const processData = (items: Item[]) => {
|
|
254
|
+
return items
|
|
255
|
+
.filter(item => item.isValid)
|
|
256
|
+
.flatMap(item =>
|
|
257
|
+
item.children
|
|
258
|
+
.filter(child => child.isActive)
|
|
259
|
+
.flatMap(child =>
|
|
260
|
+
child.properties
|
|
261
|
+
.filter(prop => prop.value !== null)
|
|
262
|
+
.map(prop => ({
|
|
263
|
+
itemId: item.id,
|
|
264
|
+
childId: child.id,
|
|
265
|
+
propValue: prop.value,
|
|
266
|
+
}))
|
|
267
|
+
)
|
|
268
|
+
)
|
|
269
|
+
}
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Pattern 6: Extract Event Handler Logic
|
|
273
|
+
|
|
274
|
+
**Before** (complexity: high in component):
|
|
275
|
+
|
|
276
|
+
```typescript
|
|
277
|
+
const Component = () => {
|
|
278
|
+
const handleSelect = (data: DataSet[]) => {
|
|
279
|
+
if (isEqual(data.map(item => item.id), dataSets.map(item => item.id))) {
|
|
280
|
+
hideSelectDataSet()
|
|
281
|
+
return
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
formattingChangedDispatcher()
|
|
285
|
+
let newDatasets = data
|
|
286
|
+
if (data.find(item => !item.name)) {
|
|
287
|
+
const newSelected = produce(data, (draft) => {
|
|
288
|
+
data.forEach((item, index) => {
|
|
289
|
+
if (!item.name) {
|
|
290
|
+
const newItem = dataSets.find(i => i.id === item.id)
|
|
291
|
+
if (newItem)
|
|
292
|
+
draft[index] = newItem
|
|
293
|
+
}
|
|
294
|
+
})
|
|
295
|
+
})
|
|
296
|
+
setDataSets(newSelected)
|
|
297
|
+
newDatasets = newSelected
|
|
298
|
+
}
|
|
299
|
+
else {
|
|
300
|
+
setDataSets(data)
|
|
301
|
+
}
|
|
302
|
+
hideSelectDataSet()
|
|
303
|
+
|
|
304
|
+
// 40 more lines of logic...
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return <div>...</div>
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
**After** (complexity: lower):
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// Extract to hook or utility
|
|
315
|
+
const useDatasetSelection = (dataSets: DataSet[], setDataSets: SetState<DataSet[]>) => {
|
|
316
|
+
const normalizeSelection = (data: DataSet[]) => {
|
|
317
|
+
const hasUnloadedItem = data.some(item => !item.name)
|
|
318
|
+
if (!hasUnloadedItem) return data
|
|
319
|
+
|
|
320
|
+
return produce(data, (draft) => {
|
|
321
|
+
data.forEach((item, index) => {
|
|
322
|
+
if (!item.name) {
|
|
323
|
+
const existing = dataSets.find(i => i.id === item.id)
|
|
324
|
+
if (existing) draft[index] = existing
|
|
325
|
+
}
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
const hasSelectionChanged = (newData: DataSet[]) => {
|
|
331
|
+
return !isEqual(
|
|
332
|
+
newData.map(item => item.id),
|
|
333
|
+
dataSets.map(item => item.id)
|
|
334
|
+
)
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return { normalizeSelection, hasSelectionChanged }
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Component becomes cleaner
|
|
341
|
+
const Component = () => {
|
|
342
|
+
const { normalizeSelection, hasSelectionChanged } = useDatasetSelection(dataSets, setDataSets)
|
|
343
|
+
|
|
344
|
+
const handleSelect = (data: DataSet[]) => {
|
|
345
|
+
if (!hasSelectionChanged(data)) {
|
|
346
|
+
hideSelectDataSet()
|
|
347
|
+
return
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
formattingChangedDispatcher()
|
|
351
|
+
const normalized = normalizeSelection(data)
|
|
352
|
+
setDataSets(normalized)
|
|
353
|
+
hideSelectDataSet()
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
return <div>...</div>
|
|
357
|
+
}
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Pattern 7: Reduce Boolean Logic Complexity
|
|
361
|
+
|
|
362
|
+
**Before** (complexity: ~8):
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
const toggleDisabled = hasInsufficientPermissions
|
|
366
|
+
|| appUnpublished
|
|
367
|
+
|| missingStartNode
|
|
368
|
+
|| triggerModeDisabled
|
|
369
|
+
|| (isAdvancedApp && !currentWorkflow?.graph)
|
|
370
|
+
|| (isBasicApp && !basicAppConfig.updated_at)
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
**After** (complexity: ~3):
|
|
374
|
+
|
|
375
|
+
```typescript
|
|
376
|
+
// Extract meaningful boolean functions
|
|
377
|
+
const isAppReady = () => {
|
|
378
|
+
if (isAdvancedApp) return !!currentWorkflow?.graph
|
|
379
|
+
return !!basicAppConfig.updated_at
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const hasRequiredPermissions = () => {
|
|
383
|
+
return isCurrentWorkspaceEditor && !hasInsufficientPermissions
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const canToggle = () => {
|
|
387
|
+
if (!hasRequiredPermissions()) return false
|
|
388
|
+
if (!isAppReady()) return false
|
|
389
|
+
if (missingStartNode) return false
|
|
390
|
+
if (triggerModeDisabled) return false
|
|
391
|
+
return true
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const toggleDisabled = !canToggle()
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
## Pattern 8: Simplify useMemo/useCallback Dependencies
|
|
398
|
+
|
|
399
|
+
**Before** (complexity: multiple recalculations):
|
|
400
|
+
|
|
401
|
+
```typescript
|
|
402
|
+
const payload = useMemo(() => {
|
|
403
|
+
let parameters: Parameter[] = []
|
|
404
|
+
let outputParameters: OutputParameter[] = []
|
|
405
|
+
|
|
406
|
+
if (!published) {
|
|
407
|
+
parameters = (inputs || []).map((item) => ({
|
|
408
|
+
name: item.variable,
|
|
409
|
+
description: '',
|
|
410
|
+
form: 'llm',
|
|
411
|
+
required: item.required,
|
|
412
|
+
type: item.type,
|
|
413
|
+
}))
|
|
414
|
+
outputParameters = (outputs || []).map((item) => ({
|
|
415
|
+
name: item.variable,
|
|
416
|
+
description: '',
|
|
417
|
+
type: item.value_type,
|
|
418
|
+
}))
|
|
419
|
+
}
|
|
420
|
+
else if (detail && detail.tool) {
|
|
421
|
+
parameters = (inputs || []).map((item) => ({
|
|
422
|
+
// Complex transformation...
|
|
423
|
+
}))
|
|
424
|
+
outputParameters = (outputs || []).map((item) => ({
|
|
425
|
+
// Complex transformation...
|
|
426
|
+
}))
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
icon: detail?.icon || icon,
|
|
431
|
+
label: detail?.label || name,
|
|
432
|
+
// ...more fields
|
|
433
|
+
}
|
|
434
|
+
}, [detail, published, workflowAppId, icon, name, description, inputs, outputs])
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
**After** (complexity: separated concerns):
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
// Separate transformations
|
|
441
|
+
const useParameterTransform = (inputs: InputVar[], detail?: ToolDetail, published?: boolean) => {
|
|
442
|
+
return useMemo(() => {
|
|
443
|
+
if (!published) {
|
|
444
|
+
return inputs.map(item => ({
|
|
445
|
+
name: item.variable,
|
|
446
|
+
description: '',
|
|
447
|
+
form: 'llm',
|
|
448
|
+
required: item.required,
|
|
449
|
+
type: item.type,
|
|
450
|
+
}))
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (!detail?.tool) return []
|
|
454
|
+
|
|
455
|
+
return inputs.map(item => ({
|
|
456
|
+
name: item.variable,
|
|
457
|
+
required: item.required,
|
|
458
|
+
type: item.type === 'paragraph' ? 'string' : item.type,
|
|
459
|
+
description: detail.tool.parameters.find(p => p.name === item.variable)?.llm_description || '',
|
|
460
|
+
form: detail.tool.parameters.find(p => p.name === item.variable)?.form || 'llm',
|
|
461
|
+
}))
|
|
462
|
+
}, [inputs, detail, published])
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Component uses hook
|
|
466
|
+
const parameters = useParameterTransform(inputs, detail, published)
|
|
467
|
+
const outputParameters = useOutputTransform(outputs, detail, published)
|
|
468
|
+
|
|
469
|
+
const payload = useMemo(() => ({
|
|
470
|
+
icon: detail?.icon || icon,
|
|
471
|
+
label: detail?.label || name,
|
|
472
|
+
parameters,
|
|
473
|
+
outputParameters,
|
|
474
|
+
// ...
|
|
475
|
+
}), [detail, icon, name, parameters, outputParameters])
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
## Target Metrics After Refactoring
|
|
479
|
+
|
|
480
|
+
| Metric | Target |
|
|
481
|
+
| ------------------ | -------------- |
|
|
482
|
+
| Total Complexity | Low complexity |
|
|
483
|
+
| Function Length | 30-100 lines |
|
|
484
|
+
| Nesting Depth | ≤ 3 levels |
|
|
485
|
+
| Conditional Chains | ≤ 3 conditions |
|