[eric] onboarding: restart-tour reload + cursor yields silently on off-script click

This commit is contained in:
ciregenz
2026-06-15 16:42:04 -07:00
parent 96f0ce78a7
commit c9efbeca39
3 changed files with 30 additions and 0 deletions
@@ -98,8 +98,20 @@ class OnboardingDirector {
controller.abort();
}
};
// Yield to the user: the runtime fires this when, during a wait for a
// SPECIFIC click target, the user instead clicks somewhere off-script. Back
// off silently (reason 'user-cancel' suppresses acRuntime's recovery popup)
// rather than nagging or auto-performing the action. It is scoped to
// click-target waits in the runtime, so it can't cancel free-interaction
// waits (e.g. connecting a model in Settings, where the user must click
// non-tour controls).
const onUserOffscript = () => {
report('step_aborted_user_offscript', { step_id: stepId });
controller.abort('user-cancel');
};
window.addEventListener('openswarm:onboarding:lost_target', onLost);
window.addEventListener('hashchange', onRouteChange);
window.addEventListener('openswarm:onboarding:user_offscript', onUserOffscript);
try {
await runStep({
@@ -115,6 +127,7 @@ class OnboardingDirector {
} finally {
window.removeEventListener('openswarm:onboarding:lost_target', onLost);
window.removeEventListener('hashchange', onRouteChange);
window.removeEventListener('openswarm:onboarding:user_offscript', onUserOffscript);
if (this.currentAbort === controller) {
this.currentAbort = null;
}
@@ -860,12 +860,25 @@ function waitForCondition(
)
) {
finish(false);
return;
}
// Off-script click during a wait for a specific target: if it's not any
// tour control and not the cursor/popup, the user has gone their own
// way, so tell the director to back off (it aborts the step silently).
// Scoped here to click-target waits so free-interaction waits
// (redux_predicate / event_bus) never cancel on a stray click.
if (!(el instanceof Element)) return;
if (el.closest('[data-onboarding], [data-select-type]')) return;
for (let n: Element | null = el; n; n = n.parentElement) {
if (parseInt(window.getComputedStyle(n).zIndex || '0', 10) >= 10500) return;
}
window.dispatchEvent(new CustomEvent('openswarm:onboarding:user_offscript', { detail: { target: cond.target } }));
};
document.addEventListener('click', handler, true);
cleanup = () => document.removeEventListener('click', handler, true);
return;
}
case 'redux_predicate': {
const check = () => {
const value = cond.selector(store.getState());
@@ -117,6 +117,10 @@ const GeneralAdvanced: React.FC<{
dispatch(resetTour());
dispatch(closeSettingsModal());
onboardingBus.emit('settings:closed');
// In-place reset can't re-arm the welcome cursor's once-per-mount
// guard, so the tour never re-fired without a reload; reload from the
// now-cleared storage is the reliable restart (matches the workaround).
window.location.reload();
}}
sx={{
color: c.text.secondary,