Changelog
2026-07-27v0.42.0· Onboarding Tour
Added
-
First-visit onboarding modal is now a real accessible dialog. The 3-step welcome that ships in
src/components/OnboardingTour.tsxnow exposesrole="dialog",aria-modal="true",aria-labelledby,aria-describedby, and a polite live region that announces "Step X of Y" to screen readers on every transition. Focus is trapped inside the panel (Tab / Shift+Tab cycle) and restored to the previously-focused element on close, so the modal never strands the user mid-board. TheClose onboardingbutton is the first thing that receives focus on open, matching how every other centered dialog in the app behaves. -
Keyboard- and click-driven dismissal paths.
Esc, backdrop click andXall dismiss the modal.Backworks on steps 2 and 3, and is hidden on step 1 to avoid a useless click target. Settings still re-opens the modal via the existingkandown:showOnboardingwindow event, with the close-on-anywhere semantics applying to that re-opening too. -
i18n the onboarding copy. Every visible string now lives under a new
onboarding.*namespace inen.json(stepLabel,back,next,getStarted,close, plus threestep<N>Title/step<N>Descpairs, 11 keys total), consumed viauseTranslation(). The same 11 keys have been translated to all 47 non-en locales; RTL locales (ar,fa,ur) wrap interpolation variables inU+200Fso screen readers and copy-paste keep them readable LTR. A new idempotent injector lives atscripts/translate-onboarding.pyfor future-maintenance use: adding a new locale is "add the locale file + add its translations dict + rerun the script". -
Per-project "first time only" gate now actually persists. A new
configLoaded: booleanflag on the Zustand store flips totrueonly afterloadConfig()(oropenServerProject, which previously bypassedloadConfigentirely) resolves with the persistedkandown.json. The onboarding modal reads that flag instead of acting on theDEFAULT_CONFIGplaceholder, so first-time visitors in a project whosekandown.jsonalready hasui.onboardingCompleted: truenever see the modal at all (no more flash-and-flicker on reload). AuserHasClosedRefguard absorbs the brief window wheresetOpen(false)and the asyncupdateConfigwrite race to re-open the modal for a single frame.
Changed
-
OnboardingTourlifecycle. TheuseEffectthat auto-shows the modal now depends on bothonboardingCompletedandconfigLoaded, so the modal cannot commit to opening until it has read the real persisted value. TheClose onboardingaria-labelis now i18n'd. Step transitions animate via the existingAnimatePresencepattern fromCheatsheet.tsx. -
Settings → "Show onboarding tour"still re-opens the modal via thekandown:showOnboardingwindow event. Closing it is a no-op write to the persistent flag when the flag is alreadytrue, so re-opening does not toggle the persisted state.