Frontmatter
| id | 9430 |
| title | TreeModel: Introduce childCount to decouple isLeaf state from emptiness |
| state | Closed |
| labels | enhancementaiarchitecturecore |
| assignees | tobiu |
| createdAt | Mar 10, 2026, 2:25 PM |
| updatedAt | Mar 10, 2026, 2:34 PM |
| githubUrl | https://github.com/neomjs/neo/issues/9430 |
| author | tobiu |
| commentsCount | 1 |
| parentIssue | 9404 |
| subIssues | [] |
| subIssuesCompleted | 0 |
| subIssuesTotal | 0 |
| blockedBy | [] |
| blocking | [] |
| closedAt | Mar 10, 2026, 2:34 PM |
TreeModel: Introduce childCount to decouple isLeaf state from emptiness
tobiu added parent issue #9404 on Mar 10, 2026, 2:25 PM

tobiu
Mar 10, 2026, 2:34 PM
Input from Gemini 3.1 Pro:
✦ I have successfully implemented
childCountonTreeModeland updated theTreeStoremutation logic.Implementation Details:
- Model: Added
childCount(Integer, default0) toNeo.data.TreeModel.- Mutation Logic: Updated
TreeStore.updateSiblingStats(parentId). Since this method is already called during every structural mutation (add,remove,move) and already iterates over all children to update ARIA sibling stats, I hooked thechildCountcalculation into this exact loop. This ensures we update the parent'schildCount(and firerecordChangeevents) optimally with O(1) overhead on an existing O(N) loop.- Soft Hydration: Updated the initialization loop in
TreeStore.splice()to injectchildCount: 0into raw data objects if it's missing, ensuring full compatibility with "Turbo Mode".Unit Tests Added: I added a new test block to
test/playwright/unit/data/TreeStore.spec.mjs:
childCount should accurately reflect the number of children and decouple from isLeaf: Verifies that adding children increaseschildCount, and removing all children dropschildCountto0while keepingisLeaf: false.- Updated the existing Turbo Mode test to verify
childCountis accurately calculated for raw objects during initial ingest.All tests are passing. I have committed the changes (
feat(data): Introduce childCount to TreeModel to decouple isLeaf state (#9430)) and will close this issue.
tobiu assigned to @tobiu on Mar 10, 2026, 2:34 PM
tobiu closed this issue on Mar 10, 2026, 2:34 PM
Goal
Introduce a
childCountfield toTreeModelto decouple a node's intrinsic type (isLeaf) from its current structural state (empty vs populated) and ensure O(1) read access for UI components.Context
Currently,
isLeafis conceptually overloaded. If a folder (branch node) loses its last child via drag-and-drop or deletion, naively reverting it toisLeaf: truewould cause the UI to render it as a file, potentially breaking drag-and-drop logic. A folder should remainisLeaf: falseforever, even when empty. However, the UI still needs to know if a branch node is empty so it can hide or disable the expand/collapse arrow.We choose
childCountover a booleanhasChildrenbecause, while it requires slightly more frequent mutations (O(1) updates on the parent duringTreeStore.splice()), it provides maximum data availability for thegrid.Rowhot-path. Developers can render badges like "Folder (5 items)" directly fromrecord.childCountwithout expensivestore.get()or#childrenMaplookups during 120fps virtual scrolling.Acceptance Criteria
childCount(Integer, default0) toNeo.data.TreeModel.TreeStore.splice()(likely withinupdateSiblingStatsor the mutation loops) to dynamically calculate and mutatechildCounton parent nodes during additions, moves, and removals.childCountdirectly on raw data objects.childCountupdates correctly when children are added or removed, while the parent'sisLeafstate remains unchanged.