1- // A one-time guided tour of the shell, shown the first time a person lands
2- // here after setup. "Seen" is a localStorage flag keyed by user id (mirrors
1+ // A guided tour of the shell, started only by explicit user action (a
2+ // command or menu item calling `openFirstRunTour`) — never automatically on
3+ // landing, which used to drop its overlay right over the chat `/` redirects
4+ // onto. "Seen" is a localStorage flag keyed by user id (mirrors
35// `command-palette-recents.ts`'s defensive access) so a shared browser
4- // profile never re-shows it for the wrong account, and finishing or
5- // skipping both mark it seen for good — there is no "remind me later".
6+ // profile never re-shows it as "new" for the wrong account, and finishing or
7+ // skipping both mark it seen — there is no "remind me later".
68
79import Joyride , { ACTIONS , type CallBackProps , STATUS , type Step } from "react-joyride" ;
8- import { useState } from "react" ;
910import { reportError } from "@corbits/error-sink" ;
11+ import { closeFirstRunTour , useFirstRunTourOpen } from "./first-run-tour-store" ;
1012
1113const STORAGE_PREFIX = "workbench.first-run-tour-seen" ;
1214
13- function hasSeenTour ( userId : string ) : boolean {
15+ /** Used only to label the menu item that opens the tour ("Take the tour" vs
16+ * "Replay tour") — no longer gates whether the tour runs. */
17+ export function hasSeenTour ( userId : string ) : boolean {
1418 try {
1519 return window . localStorage . getItem ( `${ STORAGE_PREFIX } :${ userId } ` ) === "true" ;
1620 } catch ( error ) {
1721 reportError ( error , { operation : "first_run_tour_read" } ) ;
18- return true ; // Storage disabled: never nag with a tour that can't remember itself .
22+ return true ; // Storage disabled: default to the less presumptuous label .
1923 }
2024}
2125
@@ -57,11 +61,12 @@ const STEPS: readonly Step[] = [
5761] ;
5862
5963/**
60- * Mounted once from `AppShell`. Renders nothing once the tour has already
61- * been seen for this user, so it costs nothing on every later visit.
64+ * Mounted once from `AppShell`. Renders nothing until `openFirstRunTour` is
65+ * called — never on its own, so a fresh landing on `/` never drops this
66+ * overlay over the chat the person was just redirected onto.
6267 */
6368export function FirstRunTour ( { userId } : { readonly userId : string } ) {
64- const [ run , setRun ] = useState ( ( ) => ! hasSeenTour ( userId ) ) ;
69+ const run = useFirstRunTourOpen ( ) ;
6570
6671 // Dismissing has to unmount Joyride, not just remember the dismissal:
6772 // a running Joyride keeps two portals appended to `document.body` and
@@ -71,14 +76,14 @@ export function FirstRunTour({ userId }: { readonly userId: string }) {
7176 // The tooltip's close (X) button fires action "close" without ever
7277 // moving status to FINISHED or SKIPPED, so it has to be treated as a
7378 // dismissal in its own right — otherwise closing the tour this way
74- // never persists and it replays on the next mount .
79+ // never closes it .
7580 if (
7681 data . status === STATUS . FINISHED ||
7782 data . status === STATUS . SKIPPED ||
7883 data . action === ACTIONS . CLOSE
7984 ) {
8085 markTourSeen ( userId ) ;
81- setRun ( false ) ;
86+ closeFirstRunTour ( ) ;
8287 }
8388 }
8489
0 commit comments