Skip to content

feat(onboarding): Add a guided onboarding tour for newcomers - #90

Open
cn0303 wants to merge 10 commits into
huggingface:mainfrom
cn0303:feat/onboarding-tour
Open

feat(onboarding): Add a guided onboarding tour for newcomers#90
cn0303 wants to merge 10 commits into
huggingface:mainfrom
cn0303:feat/onboarding-tour

Conversation

@cn0303

@cn0303 cn0303 commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

What this adds

A guided, interactive onboarding tour that walks a first-time user through the whole LeRobot loop
inside LeLab. From picking a robot, calibrating it, recording a dataset, training a policy to running it.
All in plain language, without them having to know the jargon or hunt for where each step lives.

Following up on our conversation about lowering the barrier for newcomers (students, hackathon
first-timers, anyone unboxing an SO-101), this is the first, self-contained piece: a real product
tour of the existing UI. It's built so a chat-style "collection assistant" can grow out of it later,
but that is deliberately out of scope here so this PR stays focused and reviewable.

Demo

onboarding-tour-demo-1080p.mp4

What the user sees

  • On the first visit, a small "Welcome to LeLab" dialog offers a ~2-minute tour (or a quiet
    dismissal). The choice is remembered, so it never nags on return.
  • Taking the tour, a spotlight dims the page and highlights the real control for each step;
    the robot selector, the calibrate button, the dataset card, the training button, the jobs list etc.
    with a short, jargon-free explanation beside it. The highlighted control stays clickable.
  • The tour follows the user's real progress: it reads whether a robot is selected, calibrated,
    a dataset has been recorded, and a model trained, then marks steps done and gently moves on. Steps
    that need hardware are optional and always skippable, so someone browsing without an arm connected
    can still complete the whole tour.
  • It can be replayed anytime from a ? launcher in the landing top bar and a floating button on
    every page.

How it works

  • No new dependencies. The spotlight, cards, and dialog are hand-rolled on the Radix/shadcn
    primitives already in the tree.
  • Almost entirely frontend. frontend/dist is left untouched (it is rebuilt from source by
    build_frontend.yml on merge). The only backend change is a small, additive scope=local mode on
    the existing /datasets endpoint (a pure filesystem scan, no Hub call), with test coverage — see
    below.
  • State-awareness stays off the Hub. Progress is derived from endpoints the app already exposes,
    and the tour polls only cheap local ones while it's open (zero cost when closed): /robots
    (is_clean), /datasets?scope=local, and /jobs. It deliberately avoids /jobs/hub and the
    Hub-merging default of /datasets; tracked cloud jobs already surface in /jobs, and auth comes
    from the existing HfAuth context. The poll pauses while the tab is hidden.
  • Coexists with the Space. The first-visit welcome is suppressed on the hosted HF Space, where the
    install modal already owns the first-run moment, so the two never stack.
  • Structure. A declarative step model (lib/onboardingSteps.ts) names the real element to
    highlight via a data-tour attribute; an OnboardingProvider drives the step machine, navigation,
    and persistence; a SpotlightOverlay tracks the target across scroll/resize and routes; a
    TourCard positions the guidance with viewport-aware flipping. Accessible: focus moves to the card,
    it announces politely, and Escape exits from anywhere.

The change is organised as small, self-contained commits, each of which type-checks on its own:
engine → cross-page navigation → progress-awareness → first-visit welcome → motion/accessibility
polish, followed by review-driven fixes (keeping the progress poll and dataset check off the Hub,
scrolling below-the-fold targets into view, and a hint correction).

Try it locally

lelab --dev

Open http://localhost:8080. To see the first-run welcome again after dismissing it, clear the flag
in the browser console and reload:

localStorage.removeItem("lelab:onboarding-v1"); location.reload()

Testing

The backend change ships with pytest coverage (tests/test_datasets.py, tests/test_server.py):
the new local-only listing is tested for its source="local" tagging and, at the endpoint level, for
never reaching the Hub on scope=local. The full suite passes.

There is no frontend test harness in the repo, so the UI was verified with tsc --noEmit, ESLint,
npm run build, and a manual walkthrough via lelab --dev: first-run offer, per-step spotlight and
navigation (including scrolling below-the-fold targets into view), progress-driven auto-advance, the
skip/escape paths for hardware-free browsing, replay from both launchers, and behaviour under
resize/scroll. The UI is browser code with no OS-specific paths, so it behaves the same on Windows,
Linux, and macOS.

cn0303 added 5 commits July 26, 2026 02:35
New users land in front of the full LeRobot pipeline at once, with no
guide through calibrate, record, train, and run. There was no onboarding
surface beyond the install modal.

Add a self-contained guided tour built on the Radix/shadcn primitives
already in the tree, so it pulls in no new dependencies. A step model
describes each stage in plain language and names the real element to
highlight via a data-tour attribute; a spotlight overlay dims the page,
cuts a hole over the target, tracks it across scroll and resize, and
positions a tour card beside it. An OnboardingProvider drives the linear
step machine and persists a seen flag, and a reusable launcher button
starts the tour from the landing top bar or a floating corner control on
any page.

This is the first, hardware-independent layer: the walkthrough runs and
is drivable end to end. Cross-page navigation, state awareness, the
first-visit welcome, and motion polish build on top in later commits.
The engine only showed steps on whatever page happened to be open, so a
step describing calibration or recording had nothing to point at unless
the user was already there.

Drive navigation from the step model: when a step is entered, route to
the page it lives on so its target can mount and be highlighted. The
calibration page needs the selected robot in navigation state to render
that robot's controls, so forward the persisted selection when heading
there. Navigation fires once per step entry rather than on every location
change, so a user who clicks elsewhere mid-step is not dragged back.
A fixed click-through can't tell whether the user actually calibrated,
recorded, or trained, so it either nags people who are already ahead or
races ahead of people who aren't.

Read a live progress snapshot from the endpoints the app already exposes
(robots, datasets, jobs, auth) and use it to drive the walkthrough. The
snapshot only polls while the tour is open, so it costs nothing when
closed and deliberately avoids a second useRobots instance. Steps carry
optional predicates: gate marks an action whose prerequisite isn't met
yet and surfaces a hint, isComplete marks it done and gently
auto-advances on the transition, and show hides steps that don't apply
(for example, the Hub step for someone already signed in). The current
step is always kept visible so a changing snapshot can never strand the
tour mid-view.
Until now the tour only started if someone found the launcher, so most
newcomers would never see it.

Show a short welcome dialog on the first visit, styled like the existing
install modal, offering the tour or a quiet dismissal. The choice is
remembered so it never nags on return. On the hosted Space the offer is
suppressed, since the non-dismissible install prompt already owns that
first-run moment and the two must not stack; the corner launcher remains
available everywhere so the tour can always be replayed.
Round off the interaction so it feels considered rather than mechanical.
Each step's card fades and scales in, honouring reduced-motion
preferences, and remounts per step so the entrance replays. Focus moves
to the card when a step opens and the card announces politely, so
keyboard and screen-reader users follow along; Escape already exits from
anywhere.
@cn0303 cn0303 changed the title Feat/onboarding tour feat(onboarding): Add a guided onboarding tour for newcomers Jul 26, 2026
cn0303 added 5 commits July 26, 2026 21:51
While the tour was open the progress snapshot polled /jobs/hub every two
seconds, and each call reaches the Hugging Face Hub (list_jobs plus
list_models). Over a couple of minutes that is dozens of Hub requests,
which runs against the care this project already takes to avoid Hub rate
limits.

Drop /jobs/hub entirely. Tracked cloud jobs already appear in the local
/jobs list, so a trained model is still detected there, and the only
thing lost is untracked past Hub models, which the tour does not need.
Authentication now comes from the existing HfAuth context rather than the
hub response. The poll also eases to three seconds, pauses while the tab
is hidden, and guards against overlapping requests, so nothing piles up.
The previous commit stopped the tour polling /jobs/hub, but its progress
snapshot still called /datasets every few seconds, and that endpoint
merges in the user's Hub datasets (list_user_datasets -> api.list_datasets)
for authenticated users. So the tour still issued a Hub request on every
poll, undercutting the earlier fix.

Give /datasets a `scope=local` mode that returns only the local-cache
listing — a pure filesystem scan, no Hub call — and point the tour poll at
it. Tracked cloud datasets aren't needed to answer "has the user recorded
anything on this machine yet." The default scope is unchanged, so existing
callers keep the merged listing. Adds coverage for both the new helper and
the endpoint branch, asserting the local path never reaches the Hub.
A step whose target sits below the fold (the cameras step on the
calibration page, the jobs step on a busy landing page) was highlighted
where it actually is — off-screen — leaving the tour card pointing at
nothing the user could see.

When a target is found, scroll it into view with block "nearest", which
moves the minimum needed and does nothing when the element is already
visible. The existing scroll listener keeps the cutout and card glued to
it as the smooth scroll settles.
The hint on a locked step told the user they could move on with Skip, but
Skip only renders for optional steps. The gated steps that most need the
hint (record, train) are required, so they show no Skip button — only
Next, which always advances.

Point the hint at Next, which is present on every step, so the guidance
matches the buttons actually on screen.
The tour deliberately does not re-navigate when the user clicks away
mid-step, expecting the spotlight to fall back to a centered card. But the
overlay only re-tracked on step or target changes, not on route changes,
so after a manual navigation it kept rendering the last-measured rect —
a highlight pinned over wherever the element used to be on the new page.

Re-run the tracking effect on route changes so it re-queries the target
(and falls back to centered when it isn't on the new page), and drop the
rect if the tracked element leaves the DOM under us, rather than pinning
it to a stale position.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant