v0.5.90: workflow duplication improvements, model allowlist, logs performance#3226
v0.5.90: workflow duplication improvements, model allowlist, logs performance#3226waleedlatif1 merged 4 commits intomainfrom
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Greptile SummaryRelease PR bundling four areas of improvement: (1) logs performance — stabilizes callbacks with refs, wraps components in
Confidence Score: 4/5
Important Files Changed
Flowchartflowchart TD
subgraph LogsPerformance["Logs Performance Optimizations"]
A["logs.tsx: useReducer + refs"] --> B["Stable callbacks (no dep changes)"]
B --> C["memo-wrapped children skip re-render"]
C --> D["LogsToolbar (memo)"]
C --> E["Dashboard (memo + structural cmp)"]
C --> F["LogsList + LogRow"]
C --> G["LogDetails (memo)"]
E --> H["StatusBar (custom equality)"]
E --> I["WorkflowsList (memo)"]
end
subgraph HoverPrefetch["Hover Prefetch Flow"]
J["LogRow onMouseEnter"] --> K["prefetchLogDetail(queryClient, logId)"]
K --> L["React Query cache populated"]
L --> M["Click → useLogDetail reads initialData from cache"]
end
subgraph Duplication["Workflow Duplication Fix"]
N["Source workflow variables"] --> O["Generate new UUIDs + build varIdMapping"]
O --> P["remapVariableIdsInSubBlocks"]
P --> Q["variables-input subblock assignments updated"]
end
subgraph ModelAllowlist["Model Allowlist"]
R["ComboBox defaultValue"] --> S{"defaultValue in evaluatedOptions?"}
S -- Yes --> T["Use defaultValue"]
S -- No --> U["Fall through to claude-sonnet-4-5 or first option"]
end
Last reviewed commit: 92b4f77 |
| const handleToggleLive = useCallback(() => { | ||
| const newIsLive = !isLive | ||
| setIsLive(newIsLive) | ||
|
|
||
| if (newIsLive) { | ||
| setIsVisuallyRefreshing(true) | ||
| setTimeout(() => setIsVisuallyRefreshing(false), REFRESH_SPINNER_DURATION_MS) | ||
| logsQuery.refetch() | ||
| if (selectedLogId) { | ||
| activeLogQuery.refetch() | ||
| setIsLive((prev) => { | ||
| if (!prev) { | ||
| setIsVisuallyRefreshing(true) | ||
| const timerId = window.setTimeout(() => { | ||
| setIsVisuallyRefreshing(false) | ||
| refreshTimersRef.current.delete(timerId) | ||
| }, REFRESH_SPINNER_DURATION_MS) | ||
| refreshTimersRef.current.add(timerId) | ||
| logsRefetchRef.current() | ||
| if (selectedLogIdRef.current) { | ||
| activeLogRefetchRef.current() | ||
| } | ||
| } | ||
| } | ||
| }, [isLive, logsQuery, activeLogQuery, selectedLogId]) | ||
| return !prev | ||
| }) | ||
| }, []) |
There was a problem hiding this comment.
Side-effects inside state updater
Calling setIsVisuallyRefreshing, window.setTimeout, and logsRefetchRef.current() inside the setIsLive updater is a side-effect within a React state updater function. While this works in practice because React batches the updates, state updaters are expected to be pure functions — React may call them more than once in strict mode or concurrent features, which would trigger duplicate timers and refetches.
Consider extracting the side-effects outside the updater:
| const handleToggleLive = useCallback(() => { | |
| const newIsLive = !isLive | |
| setIsLive(newIsLive) | |
| if (newIsLive) { | |
| setIsVisuallyRefreshing(true) | |
| setTimeout(() => setIsVisuallyRefreshing(false), REFRESH_SPINNER_DURATION_MS) | |
| logsQuery.refetch() | |
| if (selectedLogId) { | |
| activeLogQuery.refetch() | |
| setIsLive((prev) => { | |
| if (!prev) { | |
| setIsVisuallyRefreshing(true) | |
| const timerId = window.setTimeout(() => { | |
| setIsVisuallyRefreshing(false) | |
| refreshTimersRef.current.delete(timerId) | |
| }, REFRESH_SPINNER_DURATION_MS) | |
| refreshTimersRef.current.add(timerId) | |
| logsRefetchRef.current() | |
| if (selectedLogIdRef.current) { | |
| activeLogRefetchRef.current() | |
| } | |
| } | |
| } | |
| }, [isLive, logsQuery, activeLogQuery, selectedLogId]) | |
| return !prev | |
| }) | |
| }, []) | |
| const handleToggleLive = useCallback(() => { | |
| setIsLive((prev) => !prev) | |
| }, []) | |
| const isLiveRef = useRef(isLive) | |
| useEffect(() => { | |
| const wasLive = isLiveRef.current | |
| isLiveRef.current = isLive | |
| if (isLive && !wasLive) { | |
| setIsVisuallyRefreshing(true) | |
| const timerId = window.setTimeout(() => { | |
| setIsVisuallyRefreshing(false) | |
| refreshTimersRef.current.delete(timerId) | |
| }, REFRESH_SPINNER_DURATION_MS) | |
| refreshTimersRef.current.add(timerId) | |
| logsRefetchRef.current() | |
| if (selectedLogIdRef.current) { | |
| activeLogRefetchRef.current() | |
| } | |
| } | |
| }, [isLive]) |
| const [storeValue, setStoreValue] = useSubBlockValue(blockId, syntheticId) | ||
|
|
||
| const toolParamValue = toolParams?.[effectiveParamId] ?? '' | ||
| const isObjectType = OBJECT_SUBBLOCK_TYPES.has(subBlock.type) |
There was a problem hiding this comment.
Mutable ref assigned during render
Assigning onParamChangeRef.current = onParamChange during render (outside a useEffect) is a side-effect during render. In React concurrent mode or strict mode, render functions may be called multiple times before commit, which can lead to stale or inconsistent ref values.
| const isObjectType = OBJECT_SUBBLOCK_TYPES.has(subBlock.type) | |
| const onParamChangeRef = useRef(onParamChange) | |
| useEffect(() => { | |
| onParamChangeRef.current = onParamChange | |
| }) |
Uh oh!
There was an error while loading. Please reload this page.