# 0.42.0 — 2026-07-27 — "Onboarding Tour"

## Added

- **First-visit onboarding modal is now a real accessible dialog.** The
  3-step welcome that ships in `src/components/OnboardingTour.tsx` now
  exposes `role="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. The `Close onboarding` button 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
  and `X` all dismiss the modal. `Back` works 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 existing `kandown:showOnboarding` window 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 in `en.json` (`stepLabel`, `back`, `next`,
  `getStarted`, `close`, plus three `step<N>Title` / `step<N>Desc` pairs,
  11 keys total), consumed via `useTranslation()`. The same 11 keys have
  been translated to all 47 non-en locales; RTL locales (`ar`, `fa`,
  `ur`) wrap interpolation variables in `U+200F` so screen readers and
  copy-paste keep them readable LTR. A new idempotent injector lives at
  `scripts/translate-onboarding.py` for 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: boolean` flag on the Zustand store flips to `true` only
  after `loadConfig()` (or `openServerProject`, which previously
  bypassed `loadConfig` entirely) resolves with the persisted
  `kandown.json`. The onboarding modal reads that flag instead of acting
  on the `DEFAULT_CONFIG` placeholder, so first-time visitors in a
  project whose `kandown.json` already has
  `ui.onboardingCompleted: true` never see the modal at all (no more
  flash-and-flicker on reload). A `userHasClosedRef` guard absorbs the
  brief window where `setOpen(false)` and the async `updateConfig` write
  race to re-open the modal for a single frame.

## Changed

- **`OnboardingTour` lifecycle.** The `useEffect` that auto-shows the
  modal now depends on both `onboardingCompleted` and `configLoaded`,
  so the modal cannot commit to opening until it has read the real
  persisted value. The `Close onboarding` `aria-label` is now
  i18n'd. Step transitions animate via the existing `AnimatePresence`
  pattern from `Cheatsheet.tsx`.

- **`Settings → "Show onboarding tour"`** still re-opens the modal via
  the `kandown:showOnboarding` window event. Closing it is a no-op write
  to the persistent flag when the flag is already `true`, so re-opening
  does not toggle the persisted state.