@jsenv/navi 0.29.92 → 0.29.94
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/jsenv_navi.js +54 -9
- package/dist/jsenv_navi.js.map +4 -4
- package/docs/AI_INSTRUCTIONS.md +3 -2
- package/docs/control_object.md +13 -0
- package/package.json +1 -1
package/dist/jsenv_navi.js
CHANGED
|
@@ -24208,12 +24208,25 @@ const useUIStateController = (
|
|
|
24208
24208
|
} = controlInfo;
|
|
24209
24209
|
controller.value = value;
|
|
24210
24210
|
controller.defaultValue = defaultValue;
|
|
24211
|
+
// An optimistic control with work in flight keeps the user's intent on
|
|
24212
|
+
// screen: an incoming external state is the echo of an intermediate
|
|
24213
|
+
// commit (the first of two queued toggles landing), and the queued
|
|
24214
|
+
// request about to go out was built on top of the UI state —
|
|
24215
|
+
// overwriting it would both flash the superseded value and send it.
|
|
24216
|
+
// `controller.state` is still taken below: it is the last known good
|
|
24217
|
+
// state, the rollback target if the chain fails.
|
|
24218
|
+
const optimisticWorkInFlight = Boolean(
|
|
24219
|
+
props.optimistic &&
|
|
24220
|
+
(controller.actionInFlight || controller.queuedActionAllowedEvent),
|
|
24221
|
+
);
|
|
24211
24222
|
if (hasStateProp) {
|
|
24212
24223
|
controller.hasStateProp = true;
|
|
24213
24224
|
const currentState = controller.state;
|
|
24214
24225
|
if (!compareTwoJsValues(state, currentState)) {
|
|
24215
24226
|
controller.state = state;
|
|
24216
|
-
|
|
24227
|
+
if (!optimisticWorkInFlight) {
|
|
24228
|
+
controller.setUIState(state, new CustomEvent("state_prop_change"));
|
|
24229
|
+
}
|
|
24217
24230
|
}
|
|
24218
24231
|
} else {
|
|
24219
24232
|
if (controller.hasStateProp) {
|
|
@@ -24230,10 +24243,12 @@ const useUIStateController = (
|
|
|
24230
24243
|
const currentState = controller.state;
|
|
24231
24244
|
if (!compareTwoJsValues(stateFromSignal, currentState)) {
|
|
24232
24245
|
controller.state = stateFromSignal;
|
|
24233
|
-
|
|
24234
|
-
|
|
24235
|
-
|
|
24236
|
-
|
|
24246
|
+
if (!optimisticWorkInFlight) {
|
|
24247
|
+
controller.setUIState(
|
|
24248
|
+
stateFromSignal,
|
|
24249
|
+
new CustomEvent("state_prop_change"),
|
|
24250
|
+
);
|
|
24251
|
+
}
|
|
24237
24252
|
}
|
|
24238
24253
|
}
|
|
24239
24254
|
}
|
|
@@ -24664,7 +24679,7 @@ const useUIGroupStateController = (
|
|
|
24664
24679
|
|
|
24665
24680
|
// onChange and applyState live inside init so they close over the stable
|
|
24666
24681
|
// signals/pubsub without needing external refs.
|
|
24667
|
-
const onChange = (e, { notifyExternal }) => {
|
|
24682
|
+
const onChange = (e, { notifyExternal, actingChild }) => {
|
|
24668
24683
|
if (groupIsRenderingRef.current) {
|
|
24669
24684
|
// Held until the layout effect below, WITH what it asked for: a child
|
|
24670
24685
|
// whose bound signal was written from the outside changes during the
|
|
@@ -24677,6 +24692,7 @@ const useUIGroupStateController = (
|
|
|
24677
24692
|
e,
|
|
24678
24693
|
notifyExternal:
|
|
24679
24694
|
pendingChange?.notifyExternal === true ? true : notifyExternal,
|
|
24695
|
+
actingChild,
|
|
24680
24696
|
};
|
|
24681
24697
|
return;
|
|
24682
24698
|
}
|
|
@@ -24700,6 +24716,21 @@ const useUIGroupStateController = (
|
|
|
24700
24716
|
// Somebody answered: what the group is worth is what its children say
|
|
24701
24717
|
// between them, from here on.
|
|
24702
24718
|
controller.stateGivenFromAbove = false;
|
|
24719
|
+
if (resolvedDistributeChildStates) {
|
|
24720
|
+
// A group answering for all its children at once holds ONE answer
|
|
24721
|
+
// its children are views OF, rather than a value that IS what they
|
|
24722
|
+
// said: a list saying who plays and four seats saying who sits
|
|
24723
|
+
// where. One view speaking moves the answer, so the others have to
|
|
24724
|
+
// show it again — the same rule as a child arriving after the value
|
|
24725
|
+
// did, for a child that was there when the value moved without it.
|
|
24726
|
+
// The one that just acted is left alone: it is where the user put
|
|
24727
|
+
// it.
|
|
24728
|
+
controller.placeChildrenUIState(
|
|
24729
|
+
groupUIState,
|
|
24730
|
+
new CustomEvent("propagate_down_set_ui_state", { detail: {} }),
|
|
24731
|
+
{ except: actingChild },
|
|
24732
|
+
);
|
|
24733
|
+
}
|
|
24703
24734
|
applyState(groupUIState, e);
|
|
24704
24735
|
} else if (notifyExternal === "silent") {
|
|
24705
24736
|
controller.syncInternalState(groupUIState);
|
|
@@ -24780,7 +24811,7 @@ const useUIGroupStateController = (
|
|
|
24780
24811
|
// place — see warnChildAnswersForItself.
|
|
24781
24812
|
// One pass over every child, which is what a plural distribute needs:
|
|
24782
24813
|
// it is asked once, sees the whole group, and answers for all of them.
|
|
24783
|
-
placeChildrenUIState: (groupUIState, e) => {
|
|
24814
|
+
placeChildrenUIState: (groupUIState, e, { except } = {}) => {
|
|
24784
24815
|
if (!resolvedDistributeChildStates) {
|
|
24785
24816
|
for (const childUIStateController of childUIStateControllerArray) {
|
|
24786
24817
|
controller.placeChildUIState(
|
|
@@ -24802,6 +24833,9 @@ const useUIGroupStateController = (
|
|
|
24802
24833
|
return;
|
|
24803
24834
|
}
|
|
24804
24835
|
for (const childUIStateController of monitoredChildren) {
|
|
24836
|
+
if (childUIStateController === except) {
|
|
24837
|
+
continue;
|
|
24838
|
+
}
|
|
24805
24839
|
if (!stateByChild.has(childUIStateController)) {
|
|
24806
24840
|
// Not named by the answer: left where it is, the way
|
|
24807
24841
|
// CANNOT_DERIVE leaves a child a per-child distribute says
|
|
@@ -25001,7 +25035,10 @@ const useUIGroupStateController = (
|
|
|
25001
25035
|
)}`,
|
|
25002
25036
|
);
|
|
25003
25037
|
if (stateChanged) {
|
|
25004
|
-
onChange(e, {
|
|
25038
|
+
onChange(e, {
|
|
25039
|
+
notifyExternal: silent ? "silent" : true,
|
|
25040
|
+
actingChild: childUIStateController,
|
|
25041
|
+
});
|
|
25005
25042
|
} else {
|
|
25006
25043
|
controller.onUIAction(e);
|
|
25007
25044
|
}
|
|
@@ -25214,6 +25251,7 @@ const useUIGroupStateController = (
|
|
|
25214
25251
|
chainEvent(batchedEvent, pendingChange.e);
|
|
25215
25252
|
scope._onChange(batchedEvent, {
|
|
25216
25253
|
notifyExternal: pendingChange.notifyExternal,
|
|
25254
|
+
actingChild: pendingChange.actingChild,
|
|
25217
25255
|
});
|
|
25218
25256
|
}
|
|
25219
25257
|
});
|
|
@@ -27219,7 +27257,6 @@ const useInteractiveProps = (props, {
|
|
|
27219
27257
|
uiStateController.actionInFlight = false;
|
|
27220
27258
|
uiStateController.runningAction = null;
|
|
27221
27259
|
const queuedEvent = uiStateController.queuedActionAllowedEvent;
|
|
27222
|
-
uiStateController.queuedActionAllowedEvent = null;
|
|
27223
27260
|
if (!queuedEvent) {
|
|
27224
27261
|
return;
|
|
27225
27262
|
}
|
|
@@ -27227,12 +27264,20 @@ const useInteractiveProps = (props, {
|
|
|
27227
27264
|
// A failure abandons the queue: the UI is rolled back to the last
|
|
27228
27265
|
// known state (resetOnError above), and what was queued was built
|
|
27229
27266
|
// on top of the state that just failed.
|
|
27267
|
+
uiStateController.queuedActionAllowedEvent = null;
|
|
27230
27268
|
return;
|
|
27231
27269
|
}
|
|
27232
27270
|
// A microtask later, not right here: this runs inside the batch()
|
|
27233
27271
|
// that settles the action (see watchActionCompletion for the same
|
|
27234
27272
|
// constraint).
|
|
27235
27273
|
queueMicrotask(() => {
|
|
27274
|
+
// Cleared here, right before the dispatch, never earlier: between
|
|
27275
|
+
// the outcome and this microtask a render can slip in (the echo
|
|
27276
|
+
// of what the settled run committed), and the external-state gate
|
|
27277
|
+
// in ui_state_controller.js must still see work in flight or it
|
|
27278
|
+
// would overwrite the UI state the queued request is about to
|
|
27279
|
+
// send.
|
|
27280
|
+
uiStateController.queuedActionAllowedEvent = null;
|
|
27236
27281
|
executeAction(queuedEvent);
|
|
27237
27282
|
});
|
|
27238
27283
|
});
|