Skip to content

feat(onboarding): add one-time feature tips - #318

Merged
johannesjo merged 6 commits into
mainfrom
ccr-48506bea-rz7r0y
Oct 18, 2026
Merged

johannesjo merged 6 commits into
mainfrom
ccr-48506bea-rz7r0y

Conversation

@johannesjo

@johannesjo johannesjo commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

Summary

This is the first step of an optional onboarding. Rather than a tour of the app up front, which on a fresh install would point at empty panels, it shows one-time tips. Each tip appears the first time its feature has something to work on:

  • Change tour: appears by the tour button once the active task has changes and its agent has stopped working. It says which model receives the diff, since the tip stands in for the button's hover help while it shows.
  • Readiness: appears when the Finish dialog opens. It explains the evidence and checks. If the project has no checks yet, it also offers a "Configure checks" button, the same condition the evidence panel uses.
  • Delegating to sub-tasks: appears in the New Task panel once other tasks exist and the selected agent supports orchestration. It offers an example prompt.

How tips behave

  • One tip at a time. After a tip is dismissed or its feature used, the next waits a minute. A tip waits 600ms before it shows, so it doesn't pop up while a dialog opens or for a task the user is only switching past.
  • Tips float over the page with a caret pointing at their control, so they never move content.
  • Inside a dialog, the tip sits beside the panel, or else under or over it. It overlaps the panel only when there is no room around it, so it doesn't cover the dialog's buttons.
  • A tip hides while its anchor is scrolled out of view or covered by another dialog. Dialog panels now carry data-dialog-id, so a tip can tell which dialog is on top without relying on DOM order.
  • A tip follows its anchor every frame while the anchor moves, then checks every 250ms. Using the feature counts as having seen the tip. Escape dismisses a tip without closing the dialog around it, and clicking its buttons leaves focus where the user was typing.

Settings and saved state

  • featureHintsEnabled and seenFeatureHints are saved in state.json.
  • New installs start with tips on. Existing installs, whose state has no featureHintsEnabled, start with them off, so long-time users aren't suddenly shown tips.
  • A new "Show feature tips" setting turns tips on or off. Turning it on shows every tip again.
  • New installs also no longer see the keyboard shortcut migration notice, since there is nothing to migrate.

Code

  • src/store/onboarding.ts: the single tip slot, the cooldown and the seen list.
  • src/lib/feature-hint-placement.ts: pure placement logic, with unit tests.
  • src/components/FeatureHint.tsx: the tip card.

Test plan

  • npm run check and npm run check:static (typecheck, lint, knip, dependency-cruiser, Prettier)
  • npm run test:changed; the push hook also ran npm test
  • New tests:
    • store: the single slot, the cooldown after dismissing or using a feature, turning tips on again
    • saving and loading: new install versus existing install, unknown tip IDs dropped
    • placement rules
    • the tip component: settle delay, clipped anchors, dialog stacking, slot hand-off, follow loop and its cleanup, Escape, focus kept on click
    • Finish dialog wiring: the readiness tip appears, and no setup offer shows once checks exist
  • All three tips rendered with the real component and stylesheet in a Vite and Playwright harness, in dark and light themes, with wide and narrow dialogs.
  • Not run in the Electron app itself: there is no Electron binary in this environment. Worth a look at the three tips in npm run dev.

Not in this PR

  • Screen readers aren't told when a tip appears. Tips mounted on the page body are hard to reach by keyboard, so this needs a reachability design first.
  • The floating-card styling is repeated in several popovers across the app. A shared helper would touch components outside this feature.

🤖 Generated with Claude Code

https://claude.ai/code/session_017Cf9ACGvzjah4kib977pkZ

Tips point at a feature the first time it has something to work on,
rather than touring the app up front:

- change tour, once the active task has changes
- readiness and evidence, when the Finish dialog opens
- delegating to sub-tasks, in the New Task panel once other tasks
  exist and the agent supports orchestration, with an example prompt

One tip shows at a time, and only while its anchor is on screen and not
covered by a dialog. Dismissing one holds the next back for a minute.
Tips float over the page, so they never move content.

New installs get tips on; installs that predate them keep them off and
can turn them on in Settings, which also shows every tip again.

New installs also no longer see the keyboard shortcut migration notice,
since there is nothing to migrate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Cf9ACGvzjah4kib977pkZ
- Wait 600ms before a tip shows, so it does not pop up while a dialog
  opens or for a task the user is only switching past.
- Fade tips in (no motion with reduced motion) and label them as tips.
- Escape dismisses a tip without closing the dialog around it.
- The change tour tip stays away once a tour is generating or ready, and
  starting a tour from the Finish dialog also counts as seeing it.
- The readiness tip offers "Set up checks" only when the project has no
  verify command, and using the section counts as seeing it.
- The sub-task tip only promises a review before merging while
  sub-tasks are not merged automatically.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Cf9ACGvzjah4kib977pkZ
A tip now carries a caret toward the control it explains. Inside a
dialog it sits beside the panel, else under or over it, so it no longer
covers the dialog's own buttons; it overlaps only without room around.
A tip above its anchor is held by its bottom edge, so a card shorter
than estimated leaves no gap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Cf9ACGvzjah4kib977pkZ
- Follow the anchor every frame while a tip shows, so it stays attached
  through opening animations, split drags and tiles shifting; measure
  the card instead of estimating its height.
- Treat an anchor scrolled out of its container as off screen.
- Tell the top dialog from the dialog stack instead of DOM order.
- Count any open of the change tour as seen, including the brief's walk
  through, and wait until the agent stops working before suggesting it.
- Using a feature while its tip shows starts the cooldown too.
- Keep the tour's hover help out of the way of its tip.
- Clicking a tip's buttons leaves focus where the user was typing.
- Correct the readiness tip, which claimed to show merge blockers, and
  keep "Set up checks" quiet, since it leaves the Finish dialog.
- Ask for Parallel Code tasks, not sub-tasks, in the example prompt.
- Measure placement once per frame while a tip waits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Cf9ACGvzjah4kib977pkZ
When a follow step hid the tip, its cleanup ran inside that step and
cancelled the frame already running; the step then asked for another,
which nothing cancelled, so measuring went on after unmount. A shown
tip now also drops to a check every 250ms once its anchor holds still
for 30 frames, and measures its content's natural height rather than
its clamped box, which crept 2px a frame near the bottom of the window.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Cf9ACGvzjah4kib977pkZ
The readiness tip offered to set up checks whenever the project had no
verify command, even with evidence checks configured. It now uses the
evidence panel's own test and button label, Configure checks.

The change-tour tip hides the tour's hover help while it shows, so it
now names the model that receives the diff, as that help does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017Cf9ACGvzjah4kib977pkZ
@johannesjo
johannesjo merged commit fb4baea into main Oct 18, 2026
5 checks passed
@johannesjo
johannesjo deleted the ccr-48506bea-rz7r0y branch October 18, 2026 12:45
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.

2 participants