feat: Add onboarding flow#199
Open
hhvrc wants to merge 7 commits into
Open
Conversation
…rect' Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a first-run onboarding experience to the SvelteKit app, including an interactive welcome modal and an optional guided tour, while also introducing a client-side redirect for legacy hash-based URLs.
Changes:
- Introduces
WelcomeScreen.svelteand wires it into the root layout to display a multi-step onboarding modal. - Adds a
driver.js-based guided tour (welcome-tour.ts) plus global CSS theming overrides for the popovers. - Implements
legacy-hash-redirect.tsand hooks it intohooks.client.tsstartup to translate old#/...URLs to the new route structure; addsexpiring-flagsfor localStorage-backed one-time/expiry gating.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| src/routes/WelcomeScreen.svelte | New multi-step welcome modal with optional “start tour” action gated by login state. |
| src/routes/+layout.svelte | Mounts the welcome screen at the app root so it can display globally. |
| src/lib/utils/legacy-hash-redirect.ts | Adds mapping + redirect logic for legacy hash routes. |
| src/lib/tour/welcome-tour.ts | Implements the driver.js welcome tour flow and step definitions. |
| src/lib/state/expiring-flags.ts | Adds expiring localStorage-backed flags used to gate onboarding/tour. |
| src/hooks.client.ts | Runs legacy redirect and prunes expired flags during client init. |
| src/app.css | Adds a dark-theme override for driver.js popovers/buttons. |
| package.json | Adds driver.js dependency. |
| pnpm-lock.yaml | Locks driver.js dependency version and integrity metadata. |
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+95
to
+98
| function handleKeydown(e: KeyboardEvent) { | ||
| if (!open) return; | ||
| if (e.key === 'Escape') dismiss(); | ||
| else if (e.key === 'ArrowRight' || e.key === 'Enter') goNext(); |
Comment on lines
+21
to
+30
| function read(): Store { | ||
| if (typeof localStorage === 'undefined') return {}; | ||
| const raw = localStorage.getItem(STORAGE_KEY); | ||
| if (!raw) return {}; | ||
| try { | ||
| const parsed: unknown = JSON.parse(raw); | ||
| if (parsed && typeof parsed === 'object') return parsed as Store; | ||
| } catch { | ||
| // Corrupt blob; reset. | ||
| } |
Comment on lines
+72
to
+77
| export function redirectLegacyHashRoute(): void { | ||
| const hash = location.hash; | ||
| if (!hash || hash.charAt(1) !== '/') return; | ||
|
|
||
| const target = mapLegacyHashRoute(hash.slice(1)); | ||
| if (!target) return; |
| showProgress: true, | ||
| allowClose: true, | ||
| progressText: '{{current}} of {{total}}', | ||
| nextBtnText: 'Skip', |
Comment on lines
+47
to
+57
| export const expiringFlags = { | ||
| /** | ||
| * Returns the stored value for `key`, or `null` if the entry is missing or | ||
| * has expired. Expired entries are NOT pruned here (see {@link clearExpired}). | ||
| */ | ||
| get<T = unknown>(key: string): T | null { | ||
| const entry = read()[key]; | ||
| if (!entry) return null; | ||
| if (Date.now() >= entry.e) return null; | ||
| return entry.v as T; | ||
| }, |
Comment on lines
+47
to
+66
| /** | ||
| * Maps a legacy WebUI hash route (without the leading `#`) to its new path, | ||
| * or returns `null` if the path doesn't look like a legacy route. | ||
| */ | ||
| export function mapLegacyHashRoute(legacyPath: string): string | null { | ||
| if (!legacyPath.startsWith('/')) return null; | ||
|
|
||
| const qIdx = legacyPath.indexOf('?'); | ||
| const path = qIdx === -1 ? legacyPath : legacyPath.slice(0, qIdx); | ||
| const search = qIdx === -1 ? '' : legacyPath.slice(qIdx); | ||
|
|
||
| if (Object.prototype.hasOwnProperty.call(EXACT, path)) return EXACT[path] + search; | ||
|
|
||
| for (const { re, to } of PATTERNS) { | ||
| if (re.test(path)) return path.replace(re, to) + search; | ||
| } | ||
|
|
||
| // Unknown legacy route → home so the user isn't dumped on a 404. | ||
| return '/home' + search; | ||
| } |
| // Defense-in-depth: only allow same-origin, root-relative redirects. | ||
| // Reject protocol-relative (`//...`) and scheme-prefixed (`http:...`, `javascript:...`) values. | ||
| const isSafeInternalPath = target.startsWith('/') && !target.startsWith('//') && !/^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(target); | ||
| location.replace(isSafeInternalPath ? target : '/home'); |
Deploying openshockapp with
|
| Latest commit: |
f037443
|
| Status: | ✅ Deploy successful! |
| Preview URL: | https://68707c38.openshockapp.pages.dev |
| Branch Preview URL: | https://feat-onboarding-flow.openshockapp.pages.dev |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This pull request introduces a new interactive welcome tour for onboarding users.
It also adds the
driver.jslibrary for the tour, applies custom theming to match the app's dark UI, and ensures users with old-style hash URLs are redirected to the new route structure.