Project: daz
Feature: scripts-framework
Summary
Morphs Loader exposed a scripts-framework list update hazard: a DzComboBox.textChanged path can cause a large DzListView tree replacement through an observable items$, and DAZ Studio crashes while the list is deleting/rebuilding rows.
This is not a request to remove observable support from list items. Observable<TreeNode[]> is useful and Power Menu uses it successfully. The problem is that the framework currently has no guardrail or explicit mode for large structural list mutations triggered from another widget's active signal chain.
Observed repro
In daz-scripts / Morphs Loader:
- Open Morphs Loader with a populated Female manifest.
- UI builds a checked tree list with about 56 product roots and 3898 morph leaves.
- Change the source dropdown from
Genesis 8 & 8.1 Female to Genesis 8 & 8.1 Male.
- The Male manifest is missing.
- The script tries to replace/clear the product list.
- DAZ Studio crashes.
Relevant log tail:
[MorphsLoader][TurboLoader] tree ready products=56 morphs=3898 unknownProducts=25 skippedProducts=0
[MorphsLoader][Dialog] source switch deferred label="Genesis 8 & 8.1 Male"
[MorphsLoader][Source] switch start from="Genesis 8 & 8.1 Female" to="Genesis 8 & 8.1 Male"
[MorphsLoader][TurboLoader] load start path=".../Genesis8_8_1Male.product-manifest.json"
[MorphsLoader][TurboLoader] read failed path=".../Genesis8_8_1Male.product-manifest.json"
[MorphsLoader][Source] switch done to="Genesis 8 & 8.1 Male" manifestLoaded=no
[MorphsLoader][TurboLoader] clear missing-manifest products source="Genesis 8 & 8.1 Male"
Earlier variants crashed when clearing directly inside the combo signal too. Deferring by a short DzTimer was not enough.
Current framework behavior involved
ListViewBuilder.items(model.items$) connects the observable directly to list mutation:
context.items.connect((items) => {
updateList(items)
})
updateList() does incremental reconciliation against existing DzListViewItems and deletes stale rows. That is fine for many updates, but it is risky for whole-tree replacement/clear after a different widget signal changes state.
Power Menu contrast
Power Menu is a complex stable script using framework list views. Its pattern is safer:
- list data is prepared before dialog build,
- UI settings mostly trigger a separate
refreshListEvent$,
- large source-like replacement is not driven directly from a combo's
textChanged signal,
- list refresh is explicit and controlled.
This suggests the framework should keep observable items, but expose/document a safer structural-update mode.
Expected framework guardrail
Need a framework-level design for one or more of these:
- an opt-in deferred or full-rebuild mode for
ListViewBuilder.items(...),
- a clear distinction between incremental reconcile and full rebuild when the identity universe changes,
- documentation warning that large tree replacement from another widget signal should use explicit refresh/rebuild, not direct
items$ notification,
- possibly a list mutation queue owned by
ListViewBuilder, not by generic Observable.
Avoid changing all Observable dispatch to async/deferred globally. That would be too broad and could break existing scripts.
Acceptance notes
A fix should preserve Power Menu behavior and existing simple observable list usage. The target is a safe framework seam for structural list replacement, not removal of Observable<TreeNode[]> support.
Project: daz
Feature: scripts-framework
Summary
Morphs Loader exposed a scripts-framework list update hazard: a
DzComboBox.textChangedpath can cause a largeDzListViewtree replacement through an observableitems$, and DAZ Studio crashes while the list is deleting/rebuilding rows.This is not a request to remove observable support from list items.
Observable<TreeNode[]>is useful and Power Menu uses it successfully. The problem is that the framework currently has no guardrail or explicit mode for large structural list mutations triggered from another widget's active signal chain.Observed repro
In
daz-scripts/ Morphs Loader:Genesis 8 & 8.1 FemaletoGenesis 8 & 8.1 Male.Relevant log tail:
Earlier variants crashed when clearing directly inside the combo signal too. Deferring by a short
DzTimerwas not enough.Current framework behavior involved
ListViewBuilder.items(model.items$)connects the observable directly to list mutation:updateList()does incremental reconciliation against existingDzListViewItems and deletes stale rows. That is fine for many updates, but it is risky for whole-tree replacement/clear after a different widget signal changes state.Power Menu contrast
Power Menu is a complex stable script using framework list views. Its pattern is safer:
refreshListEvent$,textChangedsignal,This suggests the framework should keep observable items, but expose/document a safer structural-update mode.
Expected framework guardrail
Need a framework-level design for one or more of these:
ListViewBuilder.items(...),items$notification,ListViewBuilder, not by genericObservable.Avoid changing all
Observabledispatch to async/deferred globally. That would be too broad and could break existing scripts.Acceptance notes
A fix should preserve Power Menu behavior and existing simple observable list usage. The target is a safe framework seam for structural list replacement, not removal of
Observable<TreeNode[]>support.