Skip to content

Commit ada908f

Browse files
committed
fix(cli): resolve masonry grid disappearing on terminal resize
Fixes bug where parallel subagents in masonry grid would disappear when terminal shrinks dramatically (2→1 column transition). Root cause: Different DOM structures for single vs multi-column layouts caused React reconciliation issues during resize. Flex columns with minWidth: 0 could collapse to zero before React re-rendered. Fix: - Use unified DOM structure for all column counts - Set minWidth: MIN_COLUMN_WIDTH to prevent column collapse - Upgrade @opentui/core and @opentui/react from 0.1.70 to 0.1.74 Also: - Add React reconciliation integration tests for resize transitions - Document the fix pattern in cli/knowledge.md
1 parent eed92fb commit ada908f

File tree

8 files changed

+810
-45
lines changed

8 files changed

+810
-45
lines changed

bun.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/knowledge.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,56 @@ For columns that share space equally within a container, use the **flex trio pat
154154
- Use `width: '100%'` (string) for parent containers, not numeric values
155155
- `alignItems: 'flex-start'` prevents children from stretching to fill row height
156156

157+
### Resize Transitions: Unified DOM Structure
158+
159+
**Problem**: When terminal resizes cause column count changes (e.g., 2→1 columns), content can disappear if the component renders different DOM structures for different column counts.
160+
161+
**Root cause**: When transitioning from multi-column to single-column:
162+
1. The multi-column flex structure renders with shrinking width
163+
2. Flex columns with `minWidth: 0` collapse to zero width
164+
3. Content disappears before React can re-render with the new single-column structure
165+
166+
**Solution**: Use a **unified DOM structure** for all column counts + defensive `minWidth`:
167+
168+
```tsx
169+
// ✅ CORRECT: Same structure for 1, 2, 3, or N columns
170+
const isMultiColumn = columns > 1
171+
172+
<box style={{ flexDirection: 'row', gap: isMultiColumn ? 1 : 0, width: '100%' }}>
173+
{columnGroups.map((columnItems, idx) => (
174+
<box
175+
key={idx}
176+
style={{
177+
flexDirection: 'column',
178+
flexGrow: 1,
179+
flexShrink: 1,
180+
flexBasis: 0,
181+
minWidth: MIN_COLUMN_WIDTH, // Use constant, NOT 0!
182+
}}
183+
>
184+
{/* Column content */}
185+
</box>
186+
))}
187+
</box>
188+
```
189+
190+
**Why this works:**
191+
1. **Unified structure** = React doesn't need to reconcile different DOM trees during transitions
192+
2. **`minWidth: MIN_COLUMN_WIDTH`** = columns can't collapse to zero during the brief resize window
193+
3. Overflow protection in the layout hook handles edge cases by reducing columns when needed
194+
195+
**Anti-pattern:**
196+
```tsx
197+
// ❌ WRONG: Different DOM structures for different column counts
198+
if (columns === 1) {
199+
return <SingleColumnLayout /> // Different structure!
200+
} else {
201+
return <MultiColumnLayout /> // React must reconcile between these
202+
}
203+
```
204+
205+
The key insight: during resize, there's a timing window where the old structure is rendered with new (smaller) dimensions. A unified structure with defensive `minWidth` survives this window gracefully.
206+
157207
## OpenTUI Text Rendering Constraints
158208

159209
**CRITICAL**: OpenTUI has strict requirements for text rendering that must be followed:

cli/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@
3030
"dependencies": {
3131
"@codebuff/sdk": "workspace:*",
3232
"@gravity-ai/api": "^0.1.2",
33-
"@opentui/core": "^0.1.70",
34-
"@opentui/react": "^0.1.70",
33+
"@opentui/core": "0.1.74",
34+
"@opentui/react": "0.1.74",
3535
"@tanstack/react-query": "^5.90.12",
3636
"commander": "^14.0.1",
3737
"immer": "^10.1.3",

0 commit comments

Comments
 (0)