aboutsummaryrefslogtreecommitdiff
path: root/internal/admin/static/intereditor.js
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-07-10 18:53:26 -0400
committerhistoria <[not public]>2026-07-10 18:53:26 -0400
commitb31958c6304a25279197c1a08b924b6b44798a99 (patch)
tree35cea781069f4a5ccbd03f96cb8b0d35af056f90 /internal/admin/static/intereditor.js
parent141161781dd26bc0a84b0d50b291368b5bce0e54 (diff)
downloadthehouseoficarus-b31958c6304a25279197c1a08b924b6b44798a99.tar.gz
feat(admin): use intereditor.js for mob talk editor to further unify condition/action system
Diffstat (limited to 'internal/admin/static/intereditor.js')
-rw-r--r--internal/admin/static/intereditor.js523
1 files changed, 297 insertions, 226 deletions
diff --git a/internal/admin/static/intereditor.js b/internal/admin/static/intereditor.js
index 0e2c03d..152aef3 100644
--- a/internal/admin/static/intereditor.js
+++ b/internal/admin/static/intereditor.js
@@ -51,6 +51,132 @@ IE_ACT_TYPES.forEach(function(at) {
IE_STEP_KINDS.push(at);
});
+// ── Scope / Context system ──
+// Lets condition & action editors target data models outside the
+// default ed[secPath][idx].steps[si] arrangement (e.g. talk node/option
+// conditions and single-step actions). When a scope is registered and its
+// token is placed on a DOM root via data-ie-scope, all mutation onclicks
+// resolve their data target through the scope instead of the legacy trigger
+// path.
+
+window._ieScopes = {};
+var _ieScopeSeq = 0;
+
+function ie_registerScope(opts, token) {
+ if (!token) {
+ token = 'sc' + (++_ieScopeSeq);
+ }
+ window._ieScopes[token] = opts;
+ return token;
+}
+window.ie_registerScope = ie_registerScope;
+
+function ie_unregisterScope(token) {
+ delete window._ieScopes[token];
+}
+window.ie_unregisterScope = ie_unregisterScope;
+
+function _ie_scopeFromEl(el) {
+ if (!el || !el.closest) return null;
+ var scopeEl = el.closest('[data-ie-scope]');
+ if (!scopeEl) return null;
+ var token = scopeEl.getAttribute('data-ie-scope');
+ return window._ieScopes[token] || null;
+}
+
+// Resolve the .ie-cond-root DOM element scoped to a mutation element el.
+// Handles both the root itself and the outer addbar (which sits outside
+// .ie-cond-root as a sibling).
+function _ie_condScopeRoot(el) {
+ var root = el.closest('.ie-cond-root');
+ if (root) return root;
+ var addbar = el.closest('.ie-cond-addbar');
+ if (!addbar) return null;
+ var parent = addbar.parentElement;
+ if (!parent) return null;
+ return parent.querySelector('.ie-cond-root');
+}
+
+// Unified condition-editing context.
+// Returns { get, set, change } — `get` always syncs DOM → data store
+// before returning. Routes through a registered scope when el lives inside
+// [data-ie-scope], otherwise through the legacy ed[secPath][idx] trigger path.
+function _ie_condCtx(el, secId, secPath, idx, kind) {
+ var scope = _ie_scopeFromEl(el);
+ if (scope) {
+ return {
+ get: function() {
+ var root = _ie_condScopeRoot(el);
+ if (root) {
+ var cond = ie_collectCond(root);
+ scope.setCond(cond);
+ }
+ return scope.getCond();
+ },
+ set: function(c) { scope.setCond(c); },
+ change: function() { scope.onChange(); }
+ };
+ }
+ return {
+ get: function() {
+ var ed = window._editorData;
+ if (!ed) return null;
+ _ie_syncCond(ed, secId, secPath, idx, kind);
+ return _ie_getCond(ed, secPath, idx, kind);
+ },
+ set: function(c) {
+ var ed = window._editorData;
+ if (!ed) return;
+ _ie_setCond(ed, secPath, idx, kind, c);
+ ced_syncDOMToData(ed);
+ },
+ change: function() { window._ieRenderOnly(); }
+ };
+}
+
+// Unified action/step-editing context.
+// Returns { get, set, change }. In scope mode reads the .ie-act-root
+// container; in legacy mode operates on ed[secPath][idx].steps[si].
+function _ie_actCtx(el, secId, secPath, idx, si) {
+ var scope = _ie_scopeFromEl(el);
+ if (scope) {
+ return {
+ get: function() {
+ var container = el.closest('.ie-act-root');
+ if (!container) return scope.getStep() || {};
+ var act = ie_collectAct(container);
+ var msgs = ie_collectMessages(container);
+ var step = scope.getStep() || {};
+ if (act) Object.keys(act).forEach(function(k) { step[k] = act[k]; });
+ if (msgs !== null) step.messages = msgs; else delete step.messages;
+ scope.setStep(step);
+ return scope.getStep();
+ },
+ set: function(s) { scope.setStep(s); },
+ change: function() { scope.onChange(); }
+ };
+ }
+ return {
+ get: function() {
+ var ed = window._editorData;
+ if (!ed) return {};
+ ie_syncStep(ed, secId, secPath, idx, si);
+ if (!ed[secPath] || !ed[secPath][idx] || !ed[secPath][idx].steps || !ed[secPath][idx].steps[si]) return {};
+ return ed[secPath][idx].steps[si];
+ },
+ set: function(s) {
+ var ed = window._editorData;
+ if (!ed) return;
+ if (!ed[secPath]) ed[secPath] = [];
+ if (!ed[secPath][idx]) ed[secPath][idx] = {steps: []};
+ if (!ed[secPath][idx].steps) ed[secPath][idx].steps = [];
+ ed[secPath][idx].steps[si] = s;
+ ced_syncDOMToData(ed);
+ },
+ change: function() { window._ieRenderOnly(); }
+ };
+}
+
// ── Rendering helpers ──
// ie_renderStepAddbar: the per-step action add bar revealed by the "+" toggle.
@@ -67,9 +193,9 @@ function ie_renderStepAddbar(step, secId, idx, si, secPath) {
}
}
if (sk.key === 'messages') {
- h += '<button class="btn btn-sm ie-add-btn" onclick="ie_addMessages(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ')">+ ' + esc(sk.label) + '</button>';
+ h += '<button class="btn btn-sm ie-add-btn" onclick="ie_addMessages(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ')">+ ' + esc(sk.label) + '</button>';
} else {
- h += '<button class="btn btn-sm ie-add-btn" onclick="ie_addActEffect(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(sk.key) + '\')">+ ' + esc(sk.label) + '</button>';
+ h += '<button class="btn btn-sm ie-add-btn" onclick="ie_addActEffect(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(sk.key) + '\')">+ ' + esc(sk.label) + '</button>';
}
});
return h;
@@ -80,7 +206,7 @@ function ie_renderStepAddbar(step, secId, idx, si, secPath) {
function ie_renderBottomAddbar(secId, secPath, idx) {
var h = '';
IE_STEP_KINDS.forEach(function(sk) {
- h += '<button class="btn btn-sm ie-add-btn" onclick="ie_addStepKind(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(sk.key) + '\')">+ ' + esc(sk.label) + '</button>';
+ h += '<button class="btn btn-sm ie-add-btn" onclick="ie_addStepKind(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(sk.key) + '\')">+ ' + esc(sk.label) + '</button>';
});
return h;
}
@@ -142,7 +268,7 @@ function ie_collectDropTableBlock(container) {
}
// ie_addStepKind creates a new step initialised with a single action kind.
-function ie_addStepKind(secId, secPath, idx, kind) {
+function ie_addStepKind(el, secId, secPath, idx, kind) {
var ed = window._editorData;
if (!ed) return;
ced_syncDOMToData(ed);
@@ -387,6 +513,8 @@ function ie_collectLeaf(el) {
function ie_renderCond(condObj, secId, idx, secPath, kind, opts) {
kind = kind || 'entry';
+ opts = opts || {};
+ var scopeAttr = opts.scopeToken ? ' data-ie-scope="' + escAttr(opts.scopeToken) + '"' : '';
var h = '';
var isGroup = condObj && (condObj.all_of || condObj.any_of);
var rootChildCount = isGroup ? (condObj.all_of || condObj.any_of || []).length : 0;
@@ -395,21 +523,21 @@ function ie_renderCond(condObj, secId, idx, secPath, kind, opts) {
if (kind && kind.indexOf('step-') === 0) {
var sn = parseInt(kind.slice(5), 10);
if (!isNaN(sn)) condTitleText = 'Condition for Step ' + (sn + 1);
- } else if (kind === 'entry') {
+ } else if (kind === 'entry' && !opts.scopeToken) {
condTitleText = 'Condition for Action Block';
}
- h += '<div class="ie-cond-root">';
+ h += '<div class="ie-cond-root"' + scopeAttr + '>';
if (condTitleText) h += '<div class="ie-cond-title">' + esc(condTitleText) + '</div>';
h += ie_renderGroup(condObj, secId, secPath, idx, '', kind);
h += '</div>';
}
- var showAddbar = !isGroup || (opts && opts.alwaysOuterAddbar && rootChildCount === 0);
+ var showAddbar = (!isGroup || (opts.alwaysOuterAddbar && rootChildCount === 0)) && typeof condObj === 'object';
if (showAddbar) {
- h += '<div class="ie-cond-addbar">';
+ h += '<div class="ie-cond-addbar"' + scopeAttr + '>';
IE_COND_TYPES.forEach(function(ct) {
- h += '<button class="btn btn-sm ie-add-btn ie-cond-add-leaf" onclick="ie_addOuterLeaf(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + ct.type + '\')">+ ' + esc(ct.label) + '</button>';
+ h += '<button class="btn btn-sm ie-add-btn ie-cond-add-leaf" onclick="ie_addOuterLeaf(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + ct.type + '\')">+ ' + esc(ct.label) + '</button>';
});
- h += '<button class="btn btn-sm ie-add-btn ie-cond-add-grp" onclick="ie_addOuterGroup(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\')">+ Group</button>';
+ h += '<button class="btn btn-sm ie-add-btn ie-cond-add-grp" onclick="ie_addOuterGroup(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\')">+ Group</button>';
h += '</div>';
}
return h;
@@ -442,10 +570,10 @@ function _ie_renderGroupNode(mode, children, not, secId, secPath, idx, gpath, ki
h += '<div class="ie-cond ie-cond-empty" data-ie-gpath="' + escAttr(gpath) + '">';
h += '<div class="ie-cond-modebar ie-cond-empty-bar">';
IE_COND_TYPES.forEach(function(ct) {
- h += '<button class="btn btn-sm ie-add-btn ie-cond-add-leaf" onclick="ie_addLeaf(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\',\'' + ct.type + '\')">+ ' + esc(ct.label) + '</button>';
+ h += '<button class="btn btn-sm ie-add-btn ie-cond-add-leaf" onclick="ie_addLeaf(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\',\'' + ct.type + '\')">+ ' + esc(ct.label) + '</button>';
});
- h += '<button class="btn btn-sm ie-add-btn ie-cond-add-grp" onclick="ie_addGroup(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')">+ Group</button>';
- h += '<button class="btn btn-sm btn-danger ie-cond-grp-rm" onclick="ie_removeGroup(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')" title="Remove this group">X</button>';
+ h += '<button class="btn btn-sm ie-add-btn ie-cond-add-grp" onclick="ie_addGroup(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')">+ Group</button>';
+ h += '<button class="btn btn-sm btn-danger ie-cond-grp-rm" onclick="ie_removeGroup(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')" title="Remove this group">X</button>';
h += '</div>';
h += '</div>';
return h;
@@ -454,13 +582,13 @@ function _ie_renderGroupNode(mode, children, not, secId, secPath, idx, gpath, ki
h += '<div class="ie-cond" data-ie-gpath="' + escAttr(gpath) + '">';
h += '<div class="ie-cond-modebar">';
if (children.length >= 2) {
- h += '<select class="ie-cond-modesel" onchange="ie_toggleCondMode(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')">';
+ h += '<select class="ie-cond-modesel" onchange="ie_toggleCondMode(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')">';
h += '<option value="all_of"' + (mode === 'all_of' ? ' selected' : '') + '>All of</option>';
h += '<option value="any_of"' + (mode === 'any_of' ? ' selected' : '') + '>Any of</option>';
h += '</select>';
}
if (children.length >= 2 || not) {
- h += '<label class="ie-cond-notlbl"><input type="checkbox" class="ie-cond-not" onchange="ie_toggleCondNot(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')"' + (not ? ' checked' : '') + '> NOT</label>';
+ h += '<label class="ie-cond-notlbl"><input type="checkbox" class="ie-cond-not" onchange="ie_toggleCondNot(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')"' + (not ? ' checked' : '') + '> NOT</label>';
}
h += '</div>';
h += '<div class="ie-cond-children">';
@@ -481,9 +609,9 @@ function _ie_renderGroupNode(mode, children, not, secId, secPath, idx, gpath, ki
h += '</div>';
h += '<div class="ie-cond-addbar" data-ie-gpath="' + escAttr(gpath) + '">';
IE_COND_TYPES.forEach(function(ct) {
- h += '<button class="btn btn-sm ie-add-btn ie-cond-add-leaf" onclick="ie_addLeaf(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\',\'' + ct.type + '\')">+ ' + esc(ct.label) + '</button>';
+ h += '<button class="btn btn-sm ie-add-btn ie-cond-add-leaf" onclick="ie_addLeaf(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\',\'' + ct.type + '\')">+ ' + esc(ct.label) + '</button>';
});
- h += '<button class="btn btn-sm ie-add-btn ie-cond-add-grp" onclick="ie_addGroup(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')">+ Group</button>';
+ h += '<button class="btn btn-sm ie-add-btn ie-cond-add-grp" onclick="ie_addGroup(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\')">+ Group</button>';
h += '</div>';
h += '</div>';
return h;
@@ -516,8 +644,8 @@ function _ie_renderLeaf(leafType, leafVal, leafSub, not, secId, secPath, idx, gp
h += '<span class="ie-cond-eq">=</span>';
h += '<input class="ie-cond-subval" placeholder="exists" value="' + escAttr(ssv) + '">';
}
- h += '<label class="ie-cond-notlbl"><input type="checkbox" class="ie-cond-not" onchange="ie_toggleNotLeaf(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\',' + ciToken + ')"' + (not ? ' checked' : '') + '> NOT</label>';
- h += '<button class="btn btn-sm btn-danger ie-cond-rm" onclick="ie_removeCond(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\',' + ciToken + ')" title="Remove">X</button>';
+ h += '<label class="ie-cond-notlbl"><input type="checkbox" class="ie-cond-not" onchange="ie_toggleNotLeaf(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\',' + ciToken + ')"' + (not ? ' checked' : '') + '> NOT</label>';
+ h += '<button class="btn btn-sm btn-danger ie-cond-rm" onclick="ie_removeCond(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',\'' + escAttr(kind) + '\',\'' + escAttr(gpath) + '\',' + ciToken + ')" title="Remove">X</button>';
h += '</div>';
return h;
}
@@ -554,27 +682,27 @@ function ie_renderAct(stepObj, secId, idx, si, secPath) {
});
}
rows += '</div>';
- rows += '<button class="btn btn-sm ie-add-btn" style="margin-top:2px" onclick="ie_addKVRow(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(at.key) + '\')">+ Add</button>';
+ rows += '<button class="btn btn-sm ie-add-btn" style="margin-top:2px" onclick="ie_addKVRow(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(at.key) + '\')">+ Add</button>';
rows += '</div>';
} else if (at.kind === 'checkbox') {
rows += '<div class="ie-act-row"><span class="ie-act-label">' + esc(at.label) + '</span>';
rows += '<input type="checkbox" class="ie-act-input" data-ie-act-key="' + escAttr(at.key) + '" data-ie-act-kind="checkbox" checked hidden>';
- rows += '<button class="btn btn-sm btn-danger ie-act-rm" onclick="ie_removeActEffect(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(at.key) + '\')">X</button>';
+ rows += '<button class="btn btn-sm btn-danger ie-act-rm" onclick="ie_removeActEffect(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(at.key) + '\')">X</button>';
rows += '</div>';
} else if (at.kind === 'search') {
rows += '<div class="ie-act-row"><span class="ie-act-label">' + esc(at.label) + '</span>';
rows += '<div class="obj-search" style="position:relative;min-width:0;flex:1"><input class="ie-act-input search-input" data-ie-act-key="' + escAttr(at.key) + '" data-search-type="' + escAttr(at.entity) + '" placeholder="' + escAttr(at.hint || 'Search ' + at.entity + '...') + '" value="' + escAttr(String(val)) + '"><div class="search-results" style="display:none"></div></div>';
- rows += '<button class="btn btn-sm btn-danger ie-act-rm" onclick="ie_removeActEffect(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(at.key) + '\')">X</button>';
+ rows += '<button class="btn btn-sm btn-danger ie-act-rm" onclick="ie_removeActEffect(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(at.key) + '\')">X</button>';
rows += '</div>';
} else if (at.kind === 'droptable') {
rows += '<div class="ie-act-kv" data-ie-act-key="drop_table">';
- rows += '<div class="ie-act-kv-header"><span>Drop Table</span><button class="btn btn-sm btn-danger ie-act-rm" style="margin-left:auto" onclick="ie_removeActEffect(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'drop_table\')">X</button></div>';
- rows += ie_renderDropTableBlock(val, 'ie_addDropRow(\''+escAttr(secId)+'\',\''+escAttr(secPath)+'\','+idx+','+si+',\'item\')', 'ie_addDropRow(\''+escAttr(secId)+'\',\''+escAttr(secPath)+'\','+idx+','+si+',\'table\')', 'ie_removeDropRow(\''+escAttr(secId)+'\',\''+escAttr(secPath)+'\','+idx+','+si+',__RI__)');
+ rows += '<div class="ie-act-kv-header"><span>Drop Table</span><button class="btn btn-sm btn-danger ie-act-rm" style="margin-left:auto" onclick="ie_removeActEffect(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'drop_table\')">X</button></div>';
+ rows += ie_renderDropTableBlock(val, 'ie_addDropRow(this,\''+escAttr(secId)+'\',\''+escAttr(secPath)+'\','+idx+','+si+',\'item\')', 'ie_addDropRow(this,\''+escAttr(secId)+'\',\''+escAttr(secPath)+'\','+idx+','+si+',\'table\')', 'ie_removeDropRow(this,\''+escAttr(secId)+'\',\''+escAttr(secPath)+'\','+idx+','+si+',__RI__)');
rows += '</div>';
} else {
rows += '<div class="ie-act-row"><span class="ie-act-label">' + esc(at.label) + '</span>';
rows += '<input class="ie-act-input" data-ie-act-key="' + escAttr(at.key) + '"' + (at.kind === 'number' ? ' type="number"' : '') + ' data-ie-act-kind="' + escAttr(at.kind) + '" value="' + escAttr(String(val)) + '" placeholder="' + escAttr(at.hint || '') + '" style="flex:1">';
- rows += '<button class="btn btn-sm btn-danger ie-act-rm" onclick="ie_removeActEffect(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(at.key) + '\')">X</button>';
+ rows += '<button class="btn btn-sm btn-danger ie-act-rm" onclick="ie_removeActEffect(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',\'' + escAttr(at.key) + '\')">X</button>';
rows += '</div>';
}
});
@@ -599,15 +727,15 @@ function ie_renderMessages(step, secId, idx, si, secPath) {
h += '<div class="ie-kv-list">';
msgs.forEach(function(m, mi) {
var upBtn = mi > 0
- ? '<button class="btn btn-sm ie-msg-up" onclick="ie_moveMessage(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',' + mi + ',' + (mi - 1) + ')" title="Move up">&#9650;</button>'
+ ? '<button class="btn btn-sm ie-msg-up" onclick="ie_moveMessage(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',' + mi + ',' + (mi - 1) + ')" title="Move up">&#9650;</button>'
: '';
var dnBtn = mi < msgs.length - 1
- ? '<button class="btn btn-sm ie-msg-dn" onclick="ie_moveMessage(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',' + mi + ',' + (mi + 1) + ')" title="Move down">&#9660;</button>'
+ ? '<button class="btn btn-sm ie-msg-dn" onclick="ie_moveMessage(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',' + mi + ',' + (mi + 1) + ')" title="Move down">&#9660;</button>'
: '';
h += '<div class="ie-kv-row ie-msg-row"><input class="ie-act-msg" data-ie-msg-idx="' + mi + '" value="' + escAttr(String(m)) + '" placeholder="Shown to player" style="flex:1">' + upBtn + dnBtn + '<button class="btn btn-sm btn-danger" onclick="' + rmFn + '">X</button></div>';
});
h += '</div>';
- h += '<button class="btn btn-sm ie-add-btn" style="margin-top:2px" onclick="ie_addMessage(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ')">+ Add Message</button>';
+ h += '<button class="btn btn-sm ie-add-btn" style="margin-top:2px" onclick="ie_addMessage(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ')">+ Add Message</button>';
h += '</div>';
return h;
}
@@ -624,12 +752,12 @@ function ie_renderStepList(steps, secId, idx, secPath) {
h += '<span class="ie-step-label">Step ' + (si + 1) + '</span>';
h += '<button class="btn btn-sm ie-step-addmore" onclick="this.closest(\'.ie-step-row\').classList.toggle(\'show-addbar\')" title="Add action to this step"></button>';
if (!stepCond) {
- h += '<button class="btn btn-sm ie-add-btn" onclick="ie_addStepCond(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ')">+ Add Step Condition</button>';
+ h += '<button class="btn btn-sm ie-add-btn" onclick="ie_addStepCond(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ')">+ Add Step Condition</button>';
}
h += '<span class="ie-step-spacer"></span>';
- if (si > 0) h += '<button class="btn btn-sm ie-step-up" onclick="ie_moveStep(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',' + (si - 1) + ')" title="Move up">&#9650;</button>';
- if (si < steps.length - 1) h += '<button class="btn btn-sm ie-step-dn" onclick="ie_moveStep(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',' + (si + 1) + ')" title="Move down">&#9660;</button>';
- h += '<button class="btn btn-sm btn-danger" onclick="ie_removeStep(\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ')" title="Remove step">X</button>';
+ if (si > 0) h += '<button class="btn btn-sm ie-step-up" onclick="ie_moveStep(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',' + (si - 1) + ')" title="Move up">&#9650;</button>';
+ if (si < steps.length - 1) h += '<button class="btn btn-sm ie-step-dn" onclick="ie_moveStep(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ',' + (si + 1) + ')" title="Move down">&#9660;</button>';
+ h += '<button class="btn btn-sm btn-danger" onclick="ie_removeStep(this,\'' + escAttr(secId) + '\',\'' + escAttr(secPath) + '\',' + idx + ',' + si + ')" title="Remove step">X</button>';
h += '</div>';
if (stepCond) {
@@ -789,16 +917,12 @@ function ie_syncAllInteractions(data, secs) {
// ── Add/Remove/Toggle: Condition ──
-function ie_addLeaf(secId, secPath, idx, kind, groupPath, leafType) {
- var ed = window._editorData;
- if (!ed) return;
- _ie_syncCond(ed, secId, secPath, idx, kind);
- var cond = _ie_getCond(ed, secPath, idx, kind);
- var group = ie_condAt(cond, groupPath);
-
+function ie_addLeaf(el, secId, secPath, idx, kind, groupPath, leafType) {
+ var c = _ie_condCtx(el, secId, secPath, idx, kind);
+ var cond = c.get();
var leaf = {};
leaf[leafType] = (leafType === 'min_credits' || leafType === 'room') ? 0 : '';
-
+ var group = ie_condAt(cond, groupPath);
var newGroup;
if (!group) {
newGroup = leaf;
@@ -811,29 +935,21 @@ function ie_addLeaf(secId, secPath, idx, kind, groupPath, leafType) {
} else {
newGroup = {all_of: [group, leaf]};
}
-
- var newCond = ie_condReplace(cond, groupPath, newGroup);
- _ie_setCond(ed, secPath, idx, kind, newCond);
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ cond = ie_condReplace(cond, groupPath, newGroup);
+ c.set(cond);
+ c.change();
}
-function ie_removeCond(secId, secPath, idx, kind, groupPath, childIdx) {
- var ed = window._editorData;
- if (!ed) return;
- _ie_syncCond(ed, secId, secPath, idx, kind);
- var cond = _ie_getCond(ed, secPath, idx, kind);
-
+function ie_removeCond(el, secId, secPath, idx, kind, groupPath, childIdx) {
+ var c = _ie_condCtx(el, secId, secPath, idx, kind);
+ var cond = c.get();
if (childIdx === IE_ROOT_LEAF) {
- _ie_setCond(ed, secPath, idx, kind, null);
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ c.set(null);
+ c.change();
return;
}
-
var group = ie_condAt(cond, groupPath);
if (!group) return;
-
if (group.all_of || group.any_of) {
var mode = group.all_of ? 'all_of' : 'any_of';
var children = (group.all_of || group.any_of).slice();
@@ -848,39 +964,30 @@ function ie_removeCond(secId, secPath, idx, kind, groupPath, childIdx) {
if (group.not) newGroup.not = true;
newGroup[mode] = children;
}
- var newCond = ie_condReplace(cond, groupPath, newGroup);
- _ie_setCond(ed, secPath, idx, kind, newCond);
+ cond = ie_condReplace(cond, groupPath, newGroup);
} else {
- var nc = ie_condReplace(cond, groupPath, null);
- _ie_setCond(ed, secPath, idx, kind, nc);
+ cond = ie_condReplace(cond, groupPath, null);
}
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ c.set(cond);
+ c.change();
}
-function ie_removeGroup(secId, secPath, idx, kind, groupPath) {
- var ed = window._editorData;
- if (!ed) return;
- _ie_syncCond(ed, secId, secPath, idx, kind);
- var cond = _ie_getCond(ed, secPath, idx, kind);
-
+function ie_removeGroup(el, secId, secPath, idx, kind, groupPath) {
+ var c = _ie_condCtx(el, secId, secPath, idx, kind);
+ var cond = c.get();
if (!groupPath) {
- _ie_setCond(ed, secPath, idx, kind, null);
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ c.set(null);
+ c.change();
return;
}
-
var parts = groupPath.split('-').map(Number);
var childIdx = parts[parts.length - 1];
var parentPath = parts.slice(0, -1).join('-');
var parent = ie_condAt(cond, parentPath);
if (!parent || !(parent.all_of || parent.any_of)) return;
-
var mode = parent.all_of ? 'all_of' : 'any_of';
var children = parent[mode].slice();
children.splice(childIdx, 1);
-
var newParent;
if (children.length === 0) {
newParent = null;
@@ -891,11 +998,9 @@ function ie_removeGroup(secId, secPath, idx, kind, groupPath) {
if (parent.not) newParent.not = true;
newParent[mode] = children;
}
-
- var newCond = ie_condReplace(cond, parentPath, newParent);
- _ie_setCond(ed, secPath, idx, kind, newCond);
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ cond = ie_condReplace(cond, parentPath, newParent);
+ c.set(cond);
+ c.change();
}
function ie_addRootConditionGroup(secPath, idx, kind) {
@@ -919,19 +1024,21 @@ function ie_addRootConditionGroup(secPath, idx, kind) {
window._ieRenderOnly();
}
-function ie_addStepCond(secId, secPath, idx, si) {
+function ie_addStepCond(el, secId, secPath, idx, si) {
+ if (el && el.nodeType && _ie_scopeFromEl(el)) {
+ var c = _ie_condCtx(el, secId, secPath, idx, 'step-' + si);
+ c.get();
+ c.change();
+ return;
+ }
ie_addRootConditionGroup(secPath, idx, 'step-' + si);
}
-function ie_addGroup(secId, secPath, idx, kind, groupPath) {
- var ed = window._editorData;
- if (!ed) return;
- _ie_syncCond(ed, secId, secPath, idx, kind);
- var cond = _ie_getCond(ed, secPath, idx, kind);
+function ie_addGroup(el, secId, secPath, idx, kind, groupPath) {
+ var c = _ie_condCtx(el, secId, secPath, idx, kind);
+ var cond = c.get();
var group = ie_condAt(cond, groupPath);
-
var newSub = {all_of: []};
-
var newGroup;
if (!group) {
newGroup = newSub;
@@ -944,78 +1051,59 @@ function ie_addGroup(secId, secPath, idx, kind, groupPath) {
} else {
newGroup = {all_of: [group, newSub]};
}
-
- var newCond = ie_condReplace(cond, groupPath, newGroup);
- _ie_setCond(ed, secPath, idx, kind, newCond);
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ cond = ie_condReplace(cond, groupPath, newGroup);
+ c.set(cond);
+ c.change();
}
-function ie_addOuterLeaf(secId, secPath, idx, kind, leafType) {
- var ed = window._editorData;
- if (!ed) return;
- _ie_syncCond(ed, secId, secPath, idx, kind);
- var cond = _ie_getCond(ed, secPath, idx, kind);
-
+function ie_addOuterLeaf(el, secId, secPath, idx, kind, leafType) {
+ var c = _ie_condCtx(el, secId, secPath, idx, kind);
+ var cond = c.get();
var leaf = {};
leaf[leafType] = (leafType === 'min_credits' || leafType === 'room') ? 0 : '';
-
- var newCond;
if (!cond) {
- newCond = leaf;
+ cond = leaf;
} else {
- newCond = {all_of: [cond, leaf]};
+ cond = {all_of: [cond, leaf]};
}
-
- _ie_setCond(ed, secPath, idx, kind, newCond);
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ c.set(cond);
+ c.change();
}
-function ie_addOuterGroup(secId, secPath, idx, kind) {
- var ed = window._editorData;
- if (!ed) return;
- _ie_syncCond(ed, secId, secPath, idx, kind);
- var cond = _ie_getCond(ed, secPath, idx, kind);
-
+function ie_addOuterGroup(el, secId, secPath, idx, kind) {
+ var c = _ie_condCtx(el, secId, secPath, idx, kind);
+ var cond = c.get();
var newSub = {all_of: []};
-
- var newCond;
if (!cond) {
- newCond = newSub;
+ cond = newSub;
} else {
- newCond = {all_of: [cond, newSub]};
+ cond = {all_of: [cond, newSub]};
}
-
- _ie_setCond(ed, secPath, idx, kind, newCond);
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ c.set(cond);
+ c.change();
}
-function ie_toggleCondMode(secId, secPath, idx, kind, groupPath) {
- var ed = window._editorData;
- if (!ed) return;
- _ie_syncCond(ed, secId, secPath, idx, kind);
- window._ieRenderOnly();
+function ie_toggleCondMode(el, secId, secPath, idx, kind, groupPath) {
+ var c = _ie_condCtx(el, secId, secPath, idx, kind);
+ c.get();
+ c.change();
}
-function ie_toggleCondNot(secId, secPath, idx, kind, groupPath) {
- var ed = window._editorData;
- if (!ed) return;
- _ie_syncCond(ed, secId, secPath, idx, kind);
- window._ieRenderOnly();
+function ie_toggleCondNot(el, secId, secPath, idx, kind, groupPath) {
+ var c = _ie_condCtx(el, secId, secPath, idx, kind);
+ c.get();
+ c.change();
}
-function ie_toggleNotLeaf(secId, secPath, idx, kind, groupPath, ci) {
- var ed = window._editorData;
- if (!ed) return;
- _ie_syncCond(ed, secId, secPath, idx, kind);
- window._ieRenderOnly();
+function ie_toggleNotLeaf(el, secId, secPath, idx, kind, groupPath, ci) {
+ var c = _ie_condCtx(el, secId, secPath, idx, kind);
+ c.get();
+ c.change();
}
// ── Add/Remove: Steps ──
-function ie_removeStep(secId, secPath, idx, si) {
+function ie_removeStep(el, secId, secPath, idx, si) {
var ed = window._editorData;
if (!ed) return;
ced_syncDOMToData(ed);
@@ -1025,7 +1113,7 @@ function ie_removeStep(secId, secPath, idx, si) {
window._ieRenderCurrent();
}
-function ie_moveStep(secId, secPath, idx, fromSi, toSi) {
+function ie_moveStep(el, secId, secPath, idx, fromSi, toSi) {
var ed = window._editorData;
if (!ed) return;
ced_syncDOMToData(ed);
@@ -1049,62 +1137,54 @@ function ie_moveRow(secPath, fromIdx, toIdx) {
window._ieRenderOnly();
}
-function ie_moveMessage(secId, secPath, idx, si, fromMi, toMi) {
- var ed = window._editorData;
- if (!ed) return;
- ced_syncDOMToData(ed);
- ie_syncStep(ed, secId, secPath, idx, si);
- var step = ed[secPath] && ed[secPath][idx] && ed[secPath][idx].steps && ed[secPath][idx].steps[si];
+function ie_moveMessage(el, secId, secPath, idx, si, fromMi, toMi) {
+ var c = _ie_actCtx(el, secId, secPath, idx, si);
+ var step = c.get();
if (!step || !Array.isArray(step.messages)) return;
if (fromMi < 0 || fromMi >= step.messages.length || toMi < 0 || toMi >= step.messages.length) return;
var m = step.messages.splice(fromMi, 1)[0];
step.messages.splice(toMi, 0, m);
- window._ieRenderOnly();
+ c.set(step);
+ c.change();
}
// ── Add/Remove: Action effects ──
-function ie_addActEffect(secId, secPath, idx, si, effectKey) {
- var ed = window._editorData;
- if (!ed) return;
- ie_syncStep(ed, secId, secPath, idx, si);
- if (!ed[secPath]) ed[secPath] = [];
- if (!ed[secPath][idx]) ed[secPath][idx] = {};
- if (!ed[secPath][idx].steps) ed[secPath][idx].steps = [];
- if (!ed[secPath][idx].steps[si]) ed[secPath][idx].steps[si] = {};
- var step = ed[secPath][idx].steps[si];
-
+function ie_addActEffect(el, secId, secPath, idx, si, effectKey) {
+ var c = _ie_actCtx(el, secId, secPath, idx, si);
+ var step = c.get();
var at = IE_ACT_TYPES.find(function(a) { return a.key === effectKey; });
if (!at) return;
- if (at.kind === 'kv') {
- step[effectKey] = {};
- } else if (at.kind === 'checkbox') {
- step[effectKey] = true;
- } else if (at.kind === 'number') {
- step[effectKey] = 0;
- } else if (at.kind === 'droptable') {
- step[effectKey] = [];
- } else {
- step[effectKey] = '';
- }
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ if (at.kind === 'kv') step[effectKey] = {};
+ else if (at.kind === 'checkbox') step[effectKey] = true;
+ else if (at.kind === 'number') step[effectKey] = 0;
+ else if (at.kind === 'droptable') step[effectKey] = [];
+ else step[effectKey] = '';
+ c.set(step);
+ c.change();
}
-function ie_removeActEffect(secId, secPath, idx, si, effectKey) {
- var ed = window._editorData;
- if (!ed) return;
- ie_syncStep(ed, secId, secPath, idx, si);
- var step = ed[secPath] && ed[secPath][idx] && ed[secPath][idx].steps && ed[secPath][idx].steps[si];
- if (!step) return;
+function ie_removeActEffect(el, secId, secPath, idx, si, effectKey) {
+ var c = _ie_actCtx(el, secId, secPath, idx, si);
+ var step = c.get();
delete step[effectKey];
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ c.set(step);
+ c.change();
}
// ── Add/Remove: KV row ──
-function ie_addKVRow(secId, secPath, idx, si, effectKey) {
+function ie_addKVRow(el, secId, secPath, idx, si, effectKey) {
+ if (_ie_scopeFromEl(el)) {
+ var c = _ie_actCtx(el, secId, secPath, idx, si);
+ var step = c.get();
+ if (!step[effectKey] || typeof step[effectKey] !== 'object') step[effectKey] = {};
+ step[effectKey][''] = '';
+ c.set(step);
+ c.change();
+ return;
+ }
+
var ed = window._editorData;
if (!ed) return;
ie_syncStep(ed, secId, secPath, idx, si);
@@ -1122,67 +1202,67 @@ function ie_addKVRow(secId, secPath, idx, si, effectKey) {
}
function ie_removeKVRow(rowEl, secId, secPath, idx, si, effectKey) {
- var ed = window._editorData;
- if (!ed) return;
if (rowEl && rowEl.closest) {
var kvRow = rowEl.closest('.ie-kv-row');
if (kvRow) rowEl = kvRow;
}
- ie_syncStep(ed, secId, secPath, idx, si);
- var step = ed[secPath] && ed[secPath][idx] && ed[secPath][idx].steps && ed[secPath][idx].steps[si];
+ var c = _ie_actCtx(rowEl, secId, secPath, idx, si);
+ var step = c.get();
if (!step || !step.hasOwnProperty(effectKey)) return;
var keyEl = rowEl.querySelector('.ie-kv-key');
var k = keyEl ? keyEl.value.trim() : '';
if (k && step[effectKey].hasOwnProperty(k)) delete step[effectKey][k];
- if (Object.keys(step[effectKey]).length === 0) {
- delete step[effectKey];
- }
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ if (Object.keys(step[effectKey]).length === 0) delete step[effectKey];
+ c.set(step);
+ c.change();
}
// ── Add/Remove: Messages ──
-function ie_addMessages(secId, secPath, idx, si) {
- var ed = window._editorData;
- if (!ed) return;
- ie_syncStep(ed, secId, secPath, idx, si);
- if (!ed[secPath] || !ed[secPath][idx] || !ed[secPath][idx].steps || !ed[secPath][idx].steps[si]) return;
- var step = ed[secPath][idx].steps[si];
+function ie_addMessages(el, secId, secPath, idx, si) {
+ var c = _ie_actCtx(el, secId, secPath, idx, si);
+ var step = c.get();
if (!step.messages || !Array.isArray(step.messages)) step.messages = [];
step.messages.push('');
- window._ieRenderCurrent();
+ c.set(step);
+ c.change();
}
// ── Drop Table row add/remove ──
-function ie_addDropRow(secId, secPath, idx, si, kind) {
- var ed = window._editorData;
- if (!ed) return;
- ie_syncStep(ed, secId, secPath, idx, si);
- if (!ed[secPath] || !ed[secPath][idx] || !ed[secPath][idx].steps || !ed[secPath][idx].steps[si]) return;
- var step = ed[secPath][idx].steps[si];
+function ie_addDropRow(el, secId, secPath, idx, si, kind) {
+ var c = _ie_actCtx(el, secId, secPath, idx, si);
+ var step = c.get();
if (!Array.isArray(step.drop_table)) step.drop_table = [];
if (kind === 'table') {
step.drop_table.push({table: '', weight: 10, quantity: 1, _kind: 'table'});
} else {
step.drop_table.push({item_id: '', weight: 10, quantity: 1, _kind: 'item'});
}
- window._ieRenderOnly();
+ c.set(step);
+ c.change();
}
-function ie_removeDropRow(secId, secPath, idx, si, ri) {
- var ed = window._editorData;
- if (!ed) return;
- ie_syncStep(ed, secId, secPath, idx, si);
- if (!ed[secPath] || !ed[secPath][idx] || !ed[secPath][idx].steps || !ed[secPath][idx].steps[si]) return;
- var step = ed[secPath][idx].steps[si];
+function ie_removeDropRow(el, secId, secPath, idx, si, ri) {
+ var c = _ie_actCtx(el, secId, secPath, idx, si);
+ var step = c.get();
if (!Array.isArray(step.drop_table)) return;
step.drop_table.splice(ri, 1);
- window._ieRenderOnly();
+ c.set(step);
+ c.change();
}
-function ie_addMessage(secId, secPath, idx, si) {
+function ie_addMessage(el, secId, secPath, idx, si) {
+ if (_ie_scopeFromEl(el)) {
+ var c = _ie_actCtx(el, secId, secPath, idx, si);
+ var step = c.get();
+ if (!step.messages || !Array.isArray(step.messages)) step.messages = [];
+ step.messages.push('');
+ c.set(step);
+ c.change();
+ return;
+ }
+
var ed = window._editorData;
if (!ed) return;
ie_syncStep(ed, secId, secPath, idx, si);
@@ -1201,32 +1281,23 @@ function ie_addMessage(secId, secPath, idx, si) {
}
function ie_removeMessage(rowEl, secId, secPath, idx, si) {
- var ed = window._editorData;
- if (!ed) return;
if (rowEl && rowEl.closest) {
var kvRow = rowEl.closest('.ie-kv-row');
if (kvRow) rowEl = kvRow;
}
- ie_syncStep(ed, secId, secPath, idx, si);
- var step = ed[secPath] && ed[secPath][idx] && ed[secPath][idx].steps && ed[secPath][idx].steps[si];
+ var c = _ie_actCtx(rowEl, secId, secPath, idx, si);
+ var step = c.get();
if (!step || !Array.isArray(step.messages)) return;
-
- var card = document.querySelector('.obj-card[data-card="' + secId + '"]');
- var stepRow = card.querySelector('.obj-sub-row.ie-inter-row[data-idx="' + idx + '"] .ie-step-row[data-step="' + si + '"]');
- if (stepRow) {
- var kvRows = stepRow.querySelectorAll('.ie-act-kv[data-ie-act-key="messages"] .ie-kv-row');
- for (var ri = 0; ri < kvRows.length; ri++) {
- if (kvRows[ri] === rowEl || kvRows[ri].contains(rowEl)) {
- step.messages.splice(ri, 1);
- break;
- }
+ var kvRows = document.querySelectorAll('.ie-act-kv[data-ie-act-key="messages"] .ie-kv-row');
+ for (var ri = 0; ri < kvRows.length; ri++) {
+ if (kvRows[ri] === rowEl || kvRows[ri].contains(rowEl)) {
+ step.messages.splice(ri, 1);
+ break;
}
}
- if (step.messages.length === 0) {
- delete step.messages;
- }
- ced_syncDOMToData(ed);
- window._ieRenderOnly();
+ if (step.messages.length === 0) delete step.messages;
+ c.set(step);
+ c.change();
}
// ── Shared trigger-style array-section renderer ──